Merge mozilla-central to tracemonkey.

This commit is contained in:
Chris Leary 2011-03-21 16:36:56 -07:00
Родитель 0583e782bb 135e4ea784
Коммит 2804e75714
241 изменённых файлов: 6340 добавлений и 4065 удалений

Просмотреть файл

@ -205,6 +205,7 @@ _BROWSER_FILES = \
browser_clearplugindata.js \
browser_clearplugindata.html \
browser_clearplugindata_noage.html \
browser_popupUI.js \
browser_sanitizeDialog.js \
browser_scope.js \
browser_selectTabAtIndex.js \
@ -249,9 +250,6 @@ _BROWSER_FILES = \
test_bug628179.html \
$(NULL)
# compartment-disabled
# browser_popupUI.js \
ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
_BROWSER_FILES += \
browser_bug462289.js \

Просмотреть файл

@ -21,7 +21,7 @@ function findPopup() {
while (enumerator.hasMoreElements()) {
let win = enumerator.getNext();
if (win.content == content.wrappedJSObject.popup) {
if (win.content.wrappedJSObject == content.wrappedJSObject.popup) {
testPopupUI(win);
return;
}

335
config/find_OOM_errors.py Executable file
Просмотреть файл

@ -0,0 +1,335 @@
#!/usr/bin/env python
usage = """%prog: A test for OOM conditions in the shell.
%prog finds segfaults and other errors caused by incorrect handling of
allocation during OOM (out-of-memory) conditions.
"""
help = """Check for regressions only. This runs a set of files with a known
number of OOM errors (specified by REGRESSION_COUNT), and exits with a non-zero
result if more or less errors are found. See js/src/Makefile.in for invocation.
"""
import hashlib
import re
import shlex
import subprocess
import sys
import threading
import time
from optparse import OptionParser
#####################################################################
# Utility functions
#####################################################################
def run(args, stdin=None):
class ThreadWorker(threading.Thread):
def __init__(self, pipe):
super(ThreadWorker, self).__init__()
self.all = ""
self.pipe = pipe
self.setDaemon(True)
def run(self):
while True:
line = self.pipe.readline()
if line == '': break
else:
self.all += line
try:
if type(args) == str:
args = shlex.split(args)
args = [str(a) for a in args] # convert to strs
stdin_pipe = subprocess.PIPE if stdin else None
proc = subprocess.Popen(args, stdin=stdin_pipe, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if stdin_pipe:
proc.stdin.write(stdin)
proc.stdin.close()
stdout_worker = ThreadWorker(proc.stdout)
stderr_worker = ThreadWorker(proc.stderr)
stdout_worker.start()
stderr_worker.start()
proc.wait()
stdout_worker.join()
stderr_worker.join()
except KeyboardInterrupt, e:
sys.exit(-1)
stdout, stderr = stdout_worker.all, stderr_worker.all
result = (stdout, stderr, proc.returncode)
return result
def get_js_files():
(out, err, exit) = run('find ../jit-test/tests -name "*.js"')
if (err, exit) == ("", 0):
sys.exit("Wrong directory, run from an objdir")
return out.split()
#####################################################################
# Blacklisting
#####################################################################
def in_blacklist(sig):
return sig in blacklist
def add_to_blacklist(sig):
blacklist[sig] = blacklist.get(sig, 0)
blacklist[sig] += 1
# How often is a particular lines important for this.
def count_lines():
"""Keep track of the amount of times individual lines occur, in order to
prioritize the errors which occur most frequently."""
counts = {}
for string,count in blacklist.items():
for line in string.split("\n"):
counts[line] = counts.get(line, 0) + count
lines = []
for k,v in counts.items():
lines.append("%6d: %s" % (v,k))
lines.sort()
countlog = file("../OOM_count_log", "w")
countlog.write("\n".join(lines))
countlog.flush()
countlog.close()
#####################################################################
# Output cleaning
#####################################################################
def clean_voutput(err):
# Skip what we can't reproduce
err = re.sub(r"^--\d+-- run: /usr/bin/dsymutil \"shell/js\"$", "", err, flags=re.MULTILINE)
err = re.sub(r"^==\d+==", "", err, flags=re.MULTILINE)
err = re.sub(r"^\*\*\d+\*\*", "", err, flags=re.MULTILINE)
err = re.sub(r"^\s+by 0x[0-9A-Fa-f]+: ", "by: ", err, flags=re.MULTILINE)
err = re.sub(r"^\s+at 0x[0-9A-Fa-f]+: ", "at: ", err, flags=re.MULTILINE)
err = re.sub(r"(^\s+Address 0x)[0-9A-Fa-f]+( is not stack'd)", r"\1\2", err, flags=re.MULTILINE)
err = re.sub(r"(^\s+Invalid write of size )\d+", r"\1x", err, flags=re.MULTILINE)
err = re.sub(r"(^\s+Invalid read of size )\d+", r"\1x", err, flags=re.MULTILINE)
err = re.sub(r"(^\s+Address 0x)[0-9A-Fa-f]+( is )\d+( bytes inside a block of size )[0-9,]+( free'd)", r"\1\2\3\4", err, flags=re.MULTILINE)
# Skip the repeating bit due to the segfault
lines = []
for l in err.split('\n'):
if l == " Process terminating with default action of signal 11 (SIGSEGV)":
break
lines.append(l)
err = '\n'.join(lines)
return err
def remove_failed_allocation_backtraces(err):
lines = []
add = True
for l in err.split('\n'):
# Set start and end conditions for including text
if l == " The site of the failed allocation is:":
add = False
elif l[:2] not in ['by: ', 'at:']:
add = True
if add:
lines.append(l)
err = '\n'.join(lines)
return err
def clean_output(err):
err = re.sub(r"^js\(\d+,0x[0-9a-f]+\) malloc: \*\*\* error for object 0x[0-9a-f]+: pointer being freed was not allocated\n\*\*\* set a breakppoint in malloc_error_break to debug\n$", "pointer being freed was not allocated", err, flags=re.MULTILINE)
return err
#####################################################################
# Consts, etc
#####################################################################
command_template = 'shell/js' \
+ ' -m -j -p' \
+ ' -e "const platform=\'darwin\'; const libdir=\'../jit-test/lib/\';"' \
+ ' -f ../jit-test/lib/prolog.js' \
+ ' -f %s'
# Blacklists are things we don't want to see in our logs again (though we do
# want to count them when they happen). Whitelists we do want to see in our
# logs again, principally because the information we have isn't enough.
blacklist = {}
add_to_blacklist(r"('', '', 1)") # 1 means OOM if the shell hasn't launched yet.
add_to_blacklist(r"('', 'out of memory\n', 1)")
whitelist = set()
whitelist.add(r"('', 'out of memory\n', -11)") # -11 means OOM
whitelist.add(r"('', 'out of memory\nout of memory\n', -11)")
#####################################################################
# Program
#####################################################################
# Options
parser = OptionParser(usage=usage)
parser.add_option("-r", "--regression", action="store", metavar="REGRESSION_COUNT", help=help,
type="int", dest="regression", default=0) # TODO: support a value of zero, eventually
(OPTIONS, args) = parser.parse_args()
if OPTIONS.regression:
# TODO: This should be expanded as we get a better hang of the OOM problems.
# For now, we'll just check that the number of OOMs in one short file does not
# increase.
files = ["../jit-test/tests/arguments/args-createontrace.js"]
else:
files = get_js_files()
# Use a command-line arg to reduce the set of files
if len (args):
files = [f for f in files if f.find(args[0]) != -1]
if OPTIONS.regression:
# Don't use a logfile, this is automated for tinderbox.
log = file("../OOM_log", "w")
num_failures = 0
for f in files:
# Run it once to establish boundaries
command = (command_template + ' -O') % (f)
out, err, exit = run(command)
max = re.match(".*OOM max count: (\d+).*", out, flags=re.DOTALL).groups()[0]
max = int(max)
# OOMs don't recover well for the first 20 allocations or so.
# TODO: revisit this.
for i in range(20, max):
if OPTIONS.regression == None:
print "Testing allocation %d/%d in %s" % (i,max,f)
command = (command_template + ' -A %d') % (f, i)
out, err, exit = run(command)
# Success (5 is SM's exit code for controlled errors)
if exit == 5 and err.find("out of memory") != -1:
continue
# Failure
else:
if OPTIONS.regression:
# Just count them
num_failures += 1
continue
#########################################################################
# The regression tests ends above. The rest of this is for running the
# script manually.
#########################################################################
problem = str((out, err, exit))
if in_blacklist(problem) and problem not in whitelist:
add_to_blacklist(problem)
continue
add_to_blacklist(problem)
# Get valgrind output for a good stack trace
vcommand = "valgrind --dsymutil=yes -q --log-file=OOM_valgrind_log_file " + command
run(vcommand)
vout = file("OOM_valgrind_log_file").read()
vout = clean_voutput(vout)
sans_alloc_sites = remove_failed_allocation_backtraces(vout)
# Don't print duplicate information
if in_blacklist(sans_alloc_sites):
add_to_blacklist(sans_alloc_sites)
continue
add_to_blacklist(sans_alloc_sites)
log.write ("\n")
log.write ("\n")
log.write ("=========================================================================")
log.write ("\n")
log.write ("An allocation failure at\n\tallocation %d/%d in %s\n\tcauses problems (detected using bug 624094)" % (i, max, f))
log.write ("\n")
log.write ("\n")
log.write ("Command (from obj directory, using patch from bug 624094):\n " + command)
log.write ("\n")
log.write ("\n")
log.write ("stdout, stderr, exitcode:\n " + problem)
log.write ("\n")
log.write ("\n")
double_free = err.find("pointer being freed was not allocated") != -1
oom_detected = err.find("out of memory") != -1
multiple_oom_detected = err.find("out of memory\nout of memory") != -1
segfault_detected = exit == -11
log.write ("Diagnosis: ")
log.write ("\n")
if multiple_oom_detected:
log.write (" - Multiple OOMs reported")
log.write ("\n")
if segfault_detected:
log.write (" - segfault")
log.write ("\n")
if not oom_detected:
log.write (" - No OOM checking")
log.write ("\n")
if double_free:
log.write (" - Double free")
log.write ("\n")
log.write ("\n")
log.write ("Valgrind info:\n" + vout)
log.write ("\n")
log.write ("\n")
log.flush()
if not OPTIONS.regression == None:
count_lines()
# Do the actual regression check
if OPTIONS.regression:
expected_num_failures = OPTIONS.regression
if num_failures != expected_num_failures:
print "TEST-UNEXPECTED-FAIL |",
if num_failures > expected_num_failures:
print "More out-of-memory errors were found (%s) than expected (%d). This probably means an allocation site has been added without a NULL-check. If this is unavoidable, you can account for it by updating Makefile.in." % (num_failures, expected_num_failures),
else:
print "Congratulations, you have removed %d out-of-memory error(s) (%d remain)! Please account for it by updating Makefile.in." % (expected_num_failures - num_failures, num_failures),
sys.exit(-1)
else:
print 'TEST-PASS | find_OOM_errors | Found the expected number of OOM errors (%d)' % (expected_num_failures)

Просмотреть файл

@ -628,9 +628,7 @@ nsFrameScriptExecutor::LoadFrameScriptInternal(const nsAString& aURL)
mGlobal->GetJSObject(&global);
if (global) {
jsval val;
JS_ExecuteScript(mCx, global,
(JSScript*)JS_GetPrivate(mCx, holder->mObject),
&val);
JS_ExecuteScript(mCx, global, holder->mObject, &val);
}
}
JSContext* unused;
@ -682,15 +680,13 @@ nsFrameScriptExecutor::LoadFrameScriptInternal(const nsAString& aURL)
JSPrincipals* jsprin = nsnull;
mPrincipal->GetJSPrincipals(mCx, &jsprin);
nsContentUtils::XPConnect()->FlagSystemFilenamePrefix(url.get(), PR_TRUE);
JSScript* script =
JSObject* scriptObj =
JS_CompileUCScriptForPrincipals(mCx, nsnull, jsprin,
(jschar*)dataString.get(),
dataString.Length(),
url.get(), 1);
if (script) {
JSObject* scriptObj = JS_NewScriptObject(mCx, script);
JS_AddObjectRoot(mCx, &scriptObj);
if (scriptObj) {
nsCAutoString scheme;
uri->GetScheme(scheme);
// We don't cache data: scripts!
@ -703,9 +699,7 @@ nsFrameScriptExecutor::LoadFrameScriptInternal(const nsAString& aURL)
sCachedScripts->Put(aURL, holder);
}
jsval val;
JS_ExecuteScript(mCx, global,
(JSScript*)JS_GetPrivate(mCx, scriptObj), &val);
JS_RemoveObjectRoot(mCx, &scriptObj);
JS_ExecuteScript(mCx, global, scriptObj, &val);
}
//XXX Argh, JSPrincipals are manually refcounted!
JSPRINCIPALS_DROP(mCx, jsprin);

Просмотреть файл

@ -1542,7 +1542,7 @@ nsJSContext::CompileScript(const PRUnichar* aText,
if (ok && ((JSVersion)aVersion) != JSVERSION_UNKNOWN) {
JSAutoRequest ar(mContext);
JSScript* script =
JSObject* scriptObj =
::JS_CompileUCScriptForPrincipalsVersion(mContext,
(JSObject *)aScopeObject,
jsprin,
@ -1551,16 +1551,10 @@ nsJSContext::CompileScript(const PRUnichar* aText,
aURL,
aLineNo,
JSVersion(aVersion));
if (script) {
JSObject *scriptObject = ::JS_NewScriptObject(mContext, script);
if (scriptObject) {
NS_ASSERTION(aScriptObject.getScriptTypeID()==JAVASCRIPT,
"Expecting JS script object holder");
rv = aScriptObject.set(scriptObject);
} else {
::JS_DestroyScript(mContext, script);
script = nsnull;
}
if (scriptObj) {
NS_ASSERTION(aScriptObject.getScriptTypeID()==JAVASCRIPT,
"Expecting JS script object holder");
rv = aScriptObject.set(scriptObj);
} else {
rv = NS_ERROR_OUT_OF_MEMORY;
}
@ -1622,10 +1616,7 @@ nsJSContext::ExecuteScript(void *aScriptObject,
nsJSContext::TerminationFuncHolder holder(this);
JSAutoRequest ar(mContext);
++mExecuteDepth;
ok = ::JS_ExecuteScript(mContext,
(JSObject *)aScopeObject,
(JSScript*)::JS_GetPrivate(mContext, scriptObj),
&val);
ok = ::JS_ExecuteScript(mContext, (JSObject *)aScopeObject, scriptObj, &val);
if (ok) {
// If all went well, convert val to a string (XXXbe unless undefined?).
@ -2068,9 +2059,7 @@ nsJSContext::Serialize(nsIObjectOutputStream* aStream, void *aScriptObject)
xdr->userdata = (void*) aStream;
JSAutoRequest ar(cx);
JSScript *script = reinterpret_cast<JSScript*>
(::JS_GetPrivate(cx, mJSObject));
if (! ::JS_XDRScript(xdr, &script)) {
if (! ::JS_XDRScriptObject(xdr, &mJSObject)) {
rv = NS_ERROR_FAILURE; // likely to be a principals serialization error
} else {
// Get the encoded JSXDRState data and write it. The JSXDRState owns
@ -2132,15 +2121,8 @@ nsJSContext::Deserialize(nsIObjectInputStream* aStream,
JSAutoRequest ar(cx);
::JS_XDRMemSetData(xdr, data, size);
JSScript *script = nsnull;
if (! ::JS_XDRScript(xdr, &script)) {
if (! ::JS_XDRScriptObject(xdr, &result)) {
rv = NS_ERROR_FAILURE; // principals deserialization error?
} else {
result = ::JS_NewScriptObject(cx, script);
if (! result) {
rv = NS_ERROR_OUT_OF_MEMORY; // certain error
::JS_DestroyScript(cx, script);
}
}
// Update data in case ::JS_XDRScript called back into C++ code to

Просмотреть файл

@ -104,13 +104,7 @@ function testStringEncode() {
var pairs = getTestPairs();
for each(pair in pairs) {
var nativeResult = nativeJSON.encode(pair[1]);
var crockfordResult = crockfordJSON.stringify(pair[1]);
do_check_eq(pair[0], nativeResult);
// Don't follow json2.js handling of non-objects
if (pair[1] && (typeof pair[1] == "object")) {
do_check_eq(crockfordResult, nativeResult);
}
}
}

Просмотреть файл

@ -95,9 +95,7 @@ function testStringEncode() {
for each(pair in pairs) {
print(pair)
var nativeResult = JSON.stringify(pair[1]);
var crockfordResult = crockfordJSON.stringify(pair[1]);
do_check_eq(pair[0], nativeResult);
do_check_eq(crockfordResult, nativeResult);
}
}

Просмотреть файл

@ -255,9 +255,8 @@ nsDOMWorkerScriptLoader::ExecuteScripts(JSContext* aCx)
JSAutoRequest ar(aCx);
JSScript* script =
static_cast<JSScript*>(JS_GetPrivate(aCx, loadInfo.scriptObj.ToJSObject()));
NS_ASSERTION(script, "This shouldn't ever be null!");
JSObject* scriptObj = loadInfo.scriptObj.ToJSObject();
NS_ASSERTION(scriptObj, "This shouldn't ever be null!");
JSObject* global = mWorker->mGlobal ?
mWorker->mGlobal :
@ -270,7 +269,7 @@ nsDOMWorkerScriptLoader::ExecuteScripts(JSContext* aCx)
JS_SetOptions(aCx, JS_GetOptions(aCx) | JSOPTION_DONT_REPORT_UNCAUGHT);
jsval val;
PRBool success = JS_ExecuteScript(aCx, global, script, &val);
PRBool success = JS_ExecuteScript(aCx, global, scriptObj, &val);
JS_SetOptions(aCx, oldOpts);
@ -827,7 +826,7 @@ nsDOMWorkerScriptLoader::ScriptCompiler::Run()
JSPrincipals* principal = nsDOMWorkerSecurityManager::WorkerPrincipal();
JSScript* script =
JSObject* scriptObj =
JS_CompileUCScriptForPrincipals(cx, global, principal,
reinterpret_cast<const jschar*>
(mScriptText.BeginReading()),
@ -835,12 +834,11 @@ nsDOMWorkerScriptLoader::ScriptCompiler::Run()
JS_SetOptions(cx, oldOpts);
if (!script) {
if (!scriptObj) {
return NS_ERROR_FAILURE;
}
mScriptObj = JS_NewScriptObject(cx, script);
NS_ENSURE_STATE(mScriptObj.ToJSObject());
mScriptObj = scriptObj;
return NS_OK;
}

Просмотреть файл

@ -13,7 +13,7 @@ function run() {
cancelTimeout(timeout);
}
var match = /a(sd)f/("asdf");
var match = /a(sd)f/.exec("asdf");
if (!RegExp.$1) {
throw "RegExp.$1 didn't get set!";
cancelTimeout(timeout);

Просмотреть файл

@ -309,8 +309,7 @@ Load(JSContext *cx,
{
uintN i;
JSString *str;
JSScript *script;
JSBool ok;
JSObject *scriptObj;
jsval result;
FILE *file;
@ -332,18 +331,16 @@ Load(JSContext *cx,
JS_ReportError(cx, "cannot open file '%s' for reading", filename.ptr());
return JS_FALSE;
}
script = JS_CompileFileHandleForPrincipals(cx, obj, filename.ptr(), file,
Environment(cx)->GetPrincipal());
scriptObj = JS_CompileFileHandleForPrincipals(cx, obj, filename.ptr(), file,
Environment(cx)->GetPrincipal());
fclose(file);
if (!script)
if (!scriptObj)
return JS_FALSE;
ok = !Environment(cx)->ShouldCompileOnly()
? JS_ExecuteScript(cx, obj, script, &result)
: JS_TRUE;
JS_DestroyScript(cx, script);
if (!ok)
if (!Environment(cx)->ShouldCompileOnly() &&
!JS_ExecuteScript(cx, obj, scriptObj, &result)) {
return JS_FALSE;
}
}
JS_SET_RVAL(cx, vp, JSVAL_VOID);
return JS_TRUE;
@ -575,7 +572,7 @@ ProcessFile(JSContext *cx,
XPCShellEnvironment* env = Environment(cx);
XPCShellEnvironment::AutoContextPusher pusher(env);
JSScript *script;
JSObject *scriptObj;
jsval result;
int lineno, startline;
JSBool ok, hitEOF;
@ -615,14 +612,11 @@ ProcessFile(JSContext *cx,
return;
}
JSScript* script =
JSObject* scriptObj =
JS_CompileFileHandleForPrincipals(cx, obj, filename, file,
env->GetPrincipal());
if (script) {
if (!env->ShouldCompileOnly())
(void)JS_ExecuteScript(cx, obj, script, &result);
JS_DestroyScript(cx, script);
}
if (scriptObj && !env->ShouldCompileOnly())
(void)JS_ExecuteScript(cx, obj, scriptObj, &result);
return;
}
@ -660,14 +654,14 @@ ProcessFile(JSContext *cx,
/* Clear any pending exception from previous failed compiles. */
JS_ClearPendingException(cx);
script =
scriptObj =
JS_CompileScriptForPrincipals(cx, obj, env->GetPrincipal(), buffer,
strlen(buffer), "typein", startline);
if (script) {
if (scriptObj) {
JSErrorReporter older;
if (!env->ShouldCompileOnly()) {
ok = JS_ExecuteScript(cx, obj, script, &result);
ok = JS_ExecuteScript(cx, obj, scriptObj, &result);
if (ok && result != JSVAL_VOID) {
/* Suppress error reports from JS_ValueToString(). */
older = JS_SetErrorReporter(cx, NULL);
@ -683,7 +677,6 @@ ProcessFile(JSContext *cx,
ok = JS_FALSE;
}
}
JS_DestroyScript(cx, script);
}
} while (!hitEOF && !env->IsQuitting());
@ -1234,11 +1227,11 @@ XPCShellEnvironment::EvaluateString(const nsString& aString,
return false;
}
JSScript* script =
JSObject* scriptObj =
JS_CompileUCScriptForPrincipals(mCx, global, GetPrincipal(),
aString.get(), aString.Length(),
"typein", 0);
if (!script) {
if (!scriptObj) {
return false;
}
@ -1248,7 +1241,7 @@ XPCShellEnvironment::EvaluateString(const nsString& aString,
}
jsval result;
JSBool ok = JS_ExecuteScript(mCx, global, script, &result);
JSBool ok = JS_ExecuteScript(mCx, global, scriptObj, &result);
if (ok && result != JSVAL_VOID) {
JSErrorReporter old = JS_SetErrorReporter(mCx, NULL);
JSString* str = JS_ValueToString(mCx, result);
@ -1263,7 +1256,5 @@ XPCShellEnvironment::EvaluateString(const nsString& aString,
}
}
JS_DestroyScript(mCx, script);
return true;
}

Просмотреть файл

@ -91,6 +91,7 @@ _newJSDContext(JSRuntime* jsrt,
{
JSDContext* jsdc = NULL;
JSCrossCompartmentCall *call = NULL;
JSBool ok;
if( ! jsrt )
return NULL;
@ -148,11 +149,11 @@ _newJSDContext(JSRuntime* jsrt,
if( ! call )
goto label_newJSDContext_failure;
if( ! JS_InitStandardClasses(jsdc->dumbContext, jsdc->glob) )
goto label_newJSDContext_failure;
ok = JS_InitStandardClasses(jsdc->dumbContext, jsdc->glob);
if( call )
JS_LeaveCrossCompartmentCall(call);
JS_LeaveCrossCompartmentCall(call);
if( ! ok )
goto label_newJSDContext_failure;
JS_EndRequest(jsdc->dumbContext);
@ -169,7 +170,8 @@ label_newJSDContext_failure:
if( jsdc ) {
jsd_DestroyObjectManager(jsdc);
jsd_DestroyAtomTable(jsdc);
JS_EndRequest(jsdc->dumbContext);
if( jsdc->dumbContext )
JS_EndRequest(jsdc->dumbContext);
free(jsdc);
}
return NULL;

Просмотреть файл

@ -140,6 +140,7 @@ jsd_Constructing(JSDContext* jsdc, JSContext *cx, JSObject *obj,
JSScript* script;
JSDScript* jsdscript;
const char* ctorURL;
JSString* ctorNameStr;
const char* ctorName;
JSD_LOCK_OBJECTS(jsdc);
@ -156,11 +157,11 @@ jsd_Constructing(JSDContext* jsdc, JSContext *cx, JSObject *obj,
JSD_LOCK_SCRIPTS(jsdc);
jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, fp);
JSD_UNLOCK_SCRIPTS(jsdc);
if( jsdscript )
{
ctorName = jsd_GetScriptFunctionId(jsdc, jsdscript);
if( ctorName )
if( jsdscript && (ctorNameStr = jsd_GetScriptFunctionId(jsdc, jsdscript)) ) {
if( (ctorName = JS_EncodeString(cx, ctorNameStr)) ) {
jsdobj->ctorName = jsd_AddAtom(jsdc, ctorName);
JS_free(cx, (void *) ctorName);
}
}
jsdobj->ctorLineno = JS_GetScriptBaseLineNumber(cx, script);
}

Просмотреть файл

@ -1042,7 +1042,7 @@ jsdScript::CreatePPLineMap()
JSFunction *fun = JSD_GetJSFunction (mCx, mScript);
JSScript *script; /* In JSD compartment */
PRUint32 baseLine;
PRBool scriptOwner = PR_FALSE;
JSObject *scriptObj = NULL;
JSString *jsstr;
size_t length;
const jschar *chars;
@ -1080,11 +1080,11 @@ jsdScript::CreatePPLineMap()
JSString *jsstr;
{
JSAutoEnterCompartment ac;
JS::AutoEnterScriptCompartment ac;
if (!ac.enter(cx, script))
return nsnull;
jsstr = JS_DecompileScript (cx, JSD_GetJSScript(mCx, mScript), "ppscript", 4);
jsstr = JS_DecompileScript (cx, script, "ppscript", 4);
if (!jsstr)
return nsnull;
@ -1093,13 +1093,17 @@ jsdScript::CreatePPLineMap()
}
JS::Anchor<JSString *> kungFuDeathGrip(jsstr);
script = JS_CompileUCScript (cx, obj, chars, length, "x-jsd:ppbuffer?type=script", 1);
if (!script)
scriptObj = JS_CompileUCScript (cx, obj, chars, length, "x-jsd:ppbuffer?type=script", 1);
if (!scriptObj)
return nsnull;
scriptOwner = PR_TRUE;
script = JS_GetScriptFromObject(scriptObj);
baseLine = 1;
}
/* Make sure that a non-function script is rooted via scriptObj until the
* end of script usage. */
JS::Anchor<JSObject *> scriptAnchor(scriptObj);
PRUint32 scriptExtent = JS_GetScriptLineExtent (cx, script);
jsbytecode* firstPC = JS_LineNumberToPC (cx, script, 0);
/* allocate worst case size of map (number of lines in script + 1
@ -1130,9 +1134,6 @@ jsdScript::CreatePPLineMap()
}
}
if (scriptOwner)
JS_DestroyScript (cx, script);
mPCMapSize = lineMapSize;
return mPPLineMap = lineMap;
}
@ -1187,7 +1188,7 @@ jsdScript::GetVersion (PRInt32 *_rval)
ASSERT_VALID_EPHEMERAL;
JSContext *cx = JSD_GetDefaultJSContext (mCx);
JSScript *script = JSD_GetJSScript(mCx, mScript);
JSAutoEnterCompartment ac;
JS::AutoEnterScriptCompartment ac;
if (!ac.enter(cx, script))
return NS_ERROR_FAILURE;
*_rval = static_cast<PRInt32>(JS_GetScriptVersion(cx, script));
@ -1383,13 +1384,14 @@ jsdScript::GetFunctionSource(nsAString & aFunctionSource)
JSString *jsstr;
JSAutoEnterCompartment ac;
JS::AutoEnterScriptCompartment asc;
if (fun) {
if (!ac.enter(cx, JS_GetFunctionObject(fun)))
return NS_ERROR_FAILURE;
jsstr = JS_DecompileFunction (cx, fun, 4);
} else {
JSScript *script = JSD_GetJSScript (mCx, mScript);
if (!ac.enter(cx, script))
if (!asc.enter(cx, script))
return NS_ERROR_FAILURE;
jsstr = JS_DecompileScript (cx, script, "ppscript", 4);
}

Просмотреть файл

@ -336,6 +336,11 @@ ifeq (x86_64, $(TARGET_CPU))
ifdef _MSC_VER
ASFILES += TrampolineMasmX64.asm
endif
ifeq ($(OS_ARCH),WINNT)
ifdef GNU_CC
ASFILES += TrampolineMingwX64.s
endif
endif
ifdef SOLARIS_SUNPRO_CXX
ASFILES += TrampolineSUNWX64.s
endif
@ -581,7 +586,16 @@ check::
$(srcdir)/config/find_vanilla_new_calls $(LIBRARY)
endif
ifdef ENABLE_TRACEJIT
# Help ensure that the number of OOM errors in SpiderMonkey doesn't increase.
# If the number of OOM errors changes, update the number below. We intend this
# number to go down over time, by fixing OOMs.
ifdef DEBUG
check-ooms:
$(wildcard $(RUN_TEST_PROGRAM)) $(PYTHON) -u $(srcdir)/config/find_OOM_errors.py --regression 129
check:: check-ooms
endif
ifndef WINCE
JITFLAGS = ,m,j,mj,mjp,am,amj,amjp,amd
check::
@ -592,7 +606,6 @@ check-valgrind::
$(wildcard $(RUN_TEST_PROGRAM)) $(PYTHON) -u $(srcdir)/jit-test/jit_test.py \
--valgrind --no-slow --no-progress --tinderbox --jitflags=$(JITFLAGS) $(DIST)/bin/js$(BIN_SUFFIX)
endif
endif
DIST_GARBAGE = config.cache config.log config.status \
config/autoconf.mk \

Просмотреть файл

@ -413,14 +413,14 @@ inline void ARMAssembler::fixUpOffsets(void * buffer)
}
}
void* ARMAssembler::executableCopy(ExecutablePool* allocator)
void* ARMAssembler::executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool **poolp)
{
// 64-bit alignment is required for next constant pool and JIT code as well
m_buffer.flushWithoutBarrier(true);
if (m_buffer.uncheckedSize() & 0x7)
bkpt(0);
void * data = m_buffer.executableCopy(allocator);
void * data = m_buffer.executableAllocAndCopy(allocator, poolp);
if (data)
fixUpOffsets(data);
return data;
@ -430,16 +430,11 @@ void* ARMAssembler::executableCopy(ExecutablePool* allocator)
// offsets and literal pool loads as it goes. The buffer is assumed to be large
// enough to hold the code, and any pre-existing literal pool is assumed to
// have been flushed.
void* ARMAssembler::executableCopy(void * buffer)
void ARMAssembler::executableCopy(void * buffer)
{
if (m_buffer.oom())
return NULL;
ASSERT(m_buffer.sizeOfConstantPool() == 0);
memcpy(buffer, m_buffer.data(), m_buffer.size());
fixUpOffsets(buffer);
return buffer;
}
} // namespace JSC

Просмотреть файл

@ -972,8 +972,8 @@ namespace JSC {
return loadBranchTarget(ARMRegisters::pc, cc, useConstantPool);
}
void* executableCopy(ExecutablePool* allocator);
void* executableCopy(void* buffer);
void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool **poolp);
void executableCopy(void* buffer);
void fixUpOffsets(void* buffer);
// Patching helpers

Просмотреть файл

@ -1558,9 +1558,9 @@ public:
return m_formatter.size();
}
void* executableCopy(ExecutablePool* allocator)
void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool** poolp)
{
void* copy = m_formatter.executableCopy(allocator);
void* copy = m_formatter.executableAllocAndCopy(allocator, poolp);
unsigned jumpCount = m_jumpsToLink.size();
for (unsigned i = 0; i < jumpCount; ++i) {
@ -1909,7 +1909,9 @@ private:
size_t size() const { return m_buffer.size(); }
bool isAligned(int alignment) const { return m_buffer.isAligned(alignment); }
void* data() const { return m_buffer.data(); }
void* executableCopy(ExecutablePool* allocator) { return m_buffer.executableCopy(allocator); }
void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool** poolp) {
return m_buffer.executableAllocAndCopy(allocator, poolp);
}
bool oom() const { return m_buffer.oom(); }
private:

Просмотреть файл

@ -471,9 +471,10 @@ public:
return m_assembler.oom();
}
void* executableCopy(void* buffer)
void executableCopy(void* buffer)
{
return m_assembler.executableCopy(buffer);
ASSERT(!oom());
m_assembler.executableCopy(buffer);
}
Label label()

Просмотреть файл

@ -137,18 +137,19 @@ namespace JSC {
* The user must check for a NULL return value, which means
* no code was generated, or there was an OOM.
*/
void* executableCopy(ExecutablePool* allocator)
void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool** poolp)
{
if (m_oom)
if (m_oom || m_size == 0) {
*poolp = NULL;
return 0;
}
if (!m_size)
return 0;
void* result = allocator->alloc(m_size);
if (!result)
void* result = allocator->alloc(m_size, poolp);
if (!result) {
*poolp = NULL;
return 0;
}
JS_ASSERT(*poolp);
ExecutableAllocator::makeWritable(result, m_size);
@ -185,7 +186,7 @@ namespace JSC {
* can continue assembling into the buffer, deferring OOM checking
* until the user wants to read code out of the buffer.
*
* See also the |executableCopy| and |buffer| methods.
* See also the |executableAllocAndCopy| and |buffer| methods.
*/
void grow(int extraCapacity = 0)

Просмотреть файл

@ -194,10 +194,10 @@ public:
return AssemblerBuffer::size();
}
void* executableCopy(ExecutablePool* allocator)
void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool** poolp)
{
flushConstantPool(false);
return AssemblerBuffer::executableCopy(allocator);
return AssemblerBuffer::executableAllocAndCopy(allocator, poolp);
}
void putIntWithConstantInt(uint32_t insn, uint32_t constant, bool isReusable = false)

Просмотреть файл

@ -62,17 +62,17 @@ class LinkBuffer {
typedef MacroAssembler::DataLabelPtr DataLabelPtr;
public:
// Note: Initialization sequence is significant, since executablePool is a PassRefPtr.
// First, executablePool is copied into m_executablePool, then the initialization of
// m_code uses m_executablePool, *not* executablePool, since this is no longer valid.
LinkBuffer(MacroAssembler* masm, ExecutablePool* executablePool)
: m_executablePool(executablePool)
, m_code(executableCopy(*masm, executablePool))
, m_size(masm->m_assembler.size())
#ifndef NDEBUG
, m_completed(false)
#endif
// 'ok' should be checked after this constructor is called; it's false if OOM occurred.
LinkBuffer(MacroAssembler* masm, ExecutableAllocator* executableAllocator,
ExecutablePool** poolp, bool* ok)
{
m_code = executableAllocAndCopy(*masm, executableAllocator, poolp);
m_executablePool = *poolp;
m_size = masm->m_assembler.size(); // must come after call to executableAllocAndCopy()!
#ifndef NDEBUG
m_completed = false;
#endif
*ok = !!m_code;
}
LinkBuffer()
@ -197,9 +197,10 @@ protected:
return m_code;
}
void *executableCopy(MacroAssembler &masm, ExecutablePool *pool)
void *executableAllocAndCopy(MacroAssembler &masm, ExecutableAllocator *allocator,
ExecutablePool **poolp)
{
return masm.m_assembler.executableCopy(pool);
return masm.m_assembler.executableAllocAndCopy(allocator, poolp);
}
void performFinalization()

Просмотреть файл

@ -2474,17 +2474,14 @@ public:
return dst.m_offset - src.m_offset;
}
void* executableCopy(ExecutablePool* allocator)
void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool **poolp)
{
void* copy = m_formatter.executableCopy(allocator);
return copy;
return m_formatter.executableAllocAndCopy(allocator, poolp);
}
void* executableCopy(void* buffer)
void executableCopy(void* buffer)
{
if (m_formatter.oom())
return NULL;
return memcpy(buffer, m_formatter.buffer(), size());
memcpy(buffer, m_formatter.buffer(), size());
}
private:
@ -2829,7 +2826,9 @@ private:
bool oom() const { return m_buffer.oom(); }
bool isAligned(int alignment) const { return m_buffer.isAligned(alignment); }
void* data() const { return m_buffer.data(); }
void* executableCopy(ExecutablePool* allocator) { return m_buffer.executableCopy(allocator); }
void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool** poolp) {
return m_buffer.executableAllocAndCopy(allocator, poolp);
}
private:

Просмотреть файл

@ -30,6 +30,7 @@
namespace JSC {
size_t ExecutableAllocator::pageSize = 0;
size_t ExecutableAllocator::largeAllocSize = 0;
}

Просмотреть файл

@ -54,16 +54,6 @@
extern "C" __declspec(dllimport) void CacheRangeFlush(LPVOID pAddr, DWORD dwLength, DWORD dwFlags);
#endif
#define JIT_ALLOCATOR_PAGE_SIZE (ExecutableAllocator::pageSize)
/*
* On Windows, VirtualAlloc effectively allocates in 64K chunks. (Technically,
* it allocates in page chunks, but the starting address is always a multiple
* of 64K, so each allocation uses up 64K of address space.) So a size less
* than that would be pointless. But it turns out that 64KB is a reasonable
* size for all platforms.
*/
#define JIT_ALLOCATOR_LARGE_ALLOC_SIZE (ExecutableAllocator::pageSize * 16)
#if ENABLE_ASSEMBLER_WX_EXCLUSIVE
#define PROTECTION_FLAGS_RW (PROT_READ | PROT_WRITE)
#define PROTECTION_FLAGS_RX (PROT_READ | PROT_EXEC)
@ -72,39 +62,15 @@ extern "C" __declspec(dllimport) void CacheRangeFlush(LPVOID pAddr, DWORD dwLeng
#define INITIAL_PROTECTION_FLAGS (PROT_READ | PROT_WRITE | PROT_EXEC)
#endif
namespace JSC {
// Something included via windows.h defines a macro with this name,
// which causes the function below to fail to compile.
#ifdef _MSC_VER
# undef max
#endif
const size_t OVERSIZE_ALLOCATION = size_t(-1);
inline size_t roundUpAllocationSize(size_t request, size_t granularity)
{
if ((std::numeric_limits<size_t>::max() - granularity) <= request)
return OVERSIZE_ALLOCATION;
// Round up to next page boundary
size_t size = request + (granularity - 1);
size = size & ~(granularity - 1);
JS_ASSERT(size >= request);
return size;
}
}
#if ENABLE_ASSEMBLER
//#define DEBUG_STRESS_JSC_ALLOCATOR
namespace JSC {
// These are reference-counted. A new one (from the constructor or create)
// starts with a count of 1.
// These are reference-counted. A new one starts with a count of 1.
class ExecutablePool {
friend class ExecutableAllocator;
private:
struct Allocation {
char* pages;
@ -113,12 +79,34 @@ private:
RChunk* chunk;
#endif
};
typedef js::Vector<Allocation, 2, js::SystemAllocPolicy> AllocationList;
char* m_freePtr;
char* m_end;
Allocation m_allocation;
// Reference count for automatic reclamation.
unsigned m_refCount;
public:
// Flag for downstream use, whether to try to release references to this pool.
bool m_destroy;
// GC number in which the m_destroy flag was most recently set. Used downstream to
// remember whether m_destroy was computed for the currently active GC.
size_t m_gcNumber;
void release(bool willDestroy = false)
{
JS_ASSERT(m_refCount != 0);
JS_ASSERT_IF(willDestroy, m_refCount = 1);
if (--m_refCount == 0) {
/* We can't (easily) use js_delete() here because the destructor is private. */
this->~ExecutablePool();
js_free(this);
}
}
private:
// It should be impossible for us to roll over, because only small
// pools have multiple holders, and they have one holder per chunk
// of generated code, and they only hold 16KB or so of code.
@ -128,114 +116,130 @@ public:
++m_refCount;
}
void release()
{
JS_ASSERT(m_refCount != 0);
if (--m_refCount == 0)
js_delete(this);
}
private:
ExecutablePool(Allocation a)
: m_freePtr(a.pages), m_end(m_freePtr + a.size), m_allocation(a), m_refCount(1),
m_destroy(false), m_gcNumber(0)
{ }
static ExecutablePool* create(size_t n)
~ExecutablePool()
{
/* We can't (easily) use js_new() here because the constructor is private. */
void *memory = js_malloc(sizeof(ExecutablePool));
ExecutablePool *pool = memory ? new(memory) ExecutablePool(n) : NULL;
if (!pool || !pool->m_freePtr) {
js_delete(pool);
return NULL;
}
return pool;
if (m_allocation.pages)
ExecutablePool::systemRelease(m_allocation);
}
void* alloc(size_t n)
{
JS_ASSERT(m_freePtr <= m_end);
// Round 'n' up to a multiple of word size; if all allocations are of
// word sized quantities, then all subsequent allocations will be aligned.
n = roundUpAllocationSize(n, sizeof(void*));
if (n == OVERSIZE_ALLOCATION)
return NULL;
if (static_cast<ptrdiff_t>(n) < (m_end - m_freePtr)) {
void* result = m_freePtr;
m_freePtr += n;
return result;
}
// Insufficient space to allocate in the existing pool
// so we need allocate into a new pool
return poolAllocate(n);
JS_ASSERT(n <= available());
void *result = m_freePtr;
m_freePtr += n;
return result;
}
~ExecutablePool()
{
Allocation* end = m_pools.end();
for (Allocation* ptr = m_pools.begin(); ptr != end; ++ptr)
ExecutablePool::systemRelease(*ptr);
size_t available() const {
JS_ASSERT(m_end >= m_freePtr);
return m_end - m_freePtr;
}
size_t available() const { return (m_pools.length() > 1) ? 0 : m_end - m_freePtr; }
// Flag for downstream use, whether to try to release references to this pool.
bool m_destroy;
// GC number in which the m_destroy flag was most recently set. Used downstream to
// remember whether m_destroy was computed for the currently active GC.
size_t m_gcNumber;
private:
// On OOM, this will return an Allocation where pages is NULL.
static Allocation systemAlloc(size_t n);
static void systemRelease(const Allocation& alloc);
ExecutablePool(size_t n);
void* poolAllocate(size_t n);
char* m_freePtr;
char* m_end;
AllocationList m_pools;
};
class ExecutableAllocator {
enum ProtectionSeting { Writable, Executable };
// Initialization can fail so we use a create method instead.
ExecutableAllocator() {}
public:
static size_t pageSize;
// Returns NULL on OOM.
static ExecutableAllocator *create()
ExecutableAllocator()
{
/* We can't (easily) use js_new() here because the constructor is private. */
void *memory = js_malloc(sizeof(ExecutableAllocator));
ExecutableAllocator *allocator = memory ? new(memory) ExecutableAllocator() : NULL;
if (!allocator)
return allocator;
if (!pageSize)
intializePageSize();
ExecutablePool *pool = ExecutablePool::create(JIT_ALLOCATOR_LARGE_ALLOC_SIZE);
if (!pool) {
js_delete(allocator);
return NULL;
if (!pageSize) {
pageSize = determinePageSize();
/*
* On Windows, VirtualAlloc effectively allocates in 64K chunks.
* (Technically, it allocates in page chunks, but the starting
* address is always a multiple of 64K, so each allocation uses up
* 64K of address space.) So a size less than that would be
* pointless. But it turns out that 64KB is a reasonable size for
* all platforms. (This assumes 4KB pages.)
*/
largeAllocSize = pageSize * 16;
}
JS_ASSERT(allocator->m_smallAllocationPools.empty());
allocator->m_smallAllocationPools.append(pool);
return allocator;
JS_ASSERT(m_smallAllocationPools.empty());
}
~ExecutableAllocator()
{
for (size_t i = 0; i < m_smallAllocationPools.length(); i++)
js_delete(m_smallAllocationPools[i]);
m_smallAllocationPools[i]->release(/* willDestroy = */true);
}
// poolForSize returns reference-counted objects. The caller owns a reference
// to the object; i.e., poolForSize increments the count before returning the
// object.
// alloc() returns a pointer to some memory, and also (by reference) a
// pointer to reference-counted pool. The caller owns a reference to the
// pool; i.e. alloc() increments the count before returning the object.
void* alloc(size_t n, ExecutablePool** poolp)
{
// Round 'n' up to a multiple of word size; if all allocations are of
// word sized quantities, then all subsequent allocations will be
// aligned.
n = roundUpAllocationSize(n, sizeof(void*));
if (n == OVERSIZE_ALLOCATION) {
*poolp = NULL;
return NULL;
}
*poolp = poolForSize(n);
if (!*poolp)
return NULL;
// This alloc is infallible because poolForSize() just obtained
// (found, or created if necessary) a pool that had enough space.
void *result = (*poolp)->alloc(n);
JS_ASSERT(result);
return result;
}
private:
static size_t pageSize;
static size_t largeAllocSize;
static const size_t OVERSIZE_ALLOCATION = size_t(-1);
static size_t roundUpAllocationSize(size_t request, size_t granularity)
{
// Something included via windows.h defines a macro with this name,
// which causes the function below to fail to compile.
#ifdef _MSC_VER
# undef max
#endif
if ((std::numeric_limits<size_t>::max() - granularity) <= request)
return OVERSIZE_ALLOCATION;
// Round up to next page boundary
size_t size = request + (granularity - 1);
size = size & ~(granularity - 1);
JS_ASSERT(size >= request);
return size;
}
ExecutablePool* createPool(size_t n)
{
size_t allocSize = roundUpAllocationSize(n, pageSize);
if (allocSize == OVERSIZE_ALLOCATION)
return NULL;
#ifdef DEBUG_STRESS_JSC_ALLOCATOR
ExecutablePool::Allocation a = ExecutablePool::systemAlloc(size_t(4294967291));
#else
ExecutablePool::Allocation a = ExecutablePool::systemAlloc(allocSize);
#endif
if (!a.pages)
return NULL;
/* We can't (easily) use js_new() here because the constructor is private. */
void *memory = js_malloc(sizeof(ExecutablePool));
return memory ? new(memory) ExecutablePool(a) : NULL;
}
ExecutablePool* poolForSize(size_t n)
{
@ -258,11 +262,11 @@ public:
#endif
// If the request is large, we just provide a unshared allocator
if (n > JIT_ALLOCATOR_LARGE_ALLOC_SIZE)
return ExecutablePool::create(n);
if (n > largeAllocSize)
return createPool(n);
// Create a new allocator
ExecutablePool* pool = ExecutablePool::create(JIT_ALLOCATOR_LARGE_ALLOC_SIZE);
ExecutablePool* pool = createPool(largeAllocSize);
if (!pool)
return NULL;
// At this point, local |pool| is the owner.
@ -295,6 +299,7 @@ public:
return pool;
}
public:
#if ENABLE_ASSEMBLER_WX_EXCLUSIVE
static void makeWritable(void* start, size_t size)
{
@ -404,61 +409,9 @@ private:
static const size_t maxSmallPools = 4;
typedef js::Vector<ExecutablePool *, maxSmallPools, js::SystemAllocPolicy > SmallExecPoolVector;
SmallExecPoolVector m_smallAllocationPools;
static void intializePageSize();
static size_t determinePageSize();
};
// This constructor can fail due to OOM. If it does, m_freePtr will be
// set to NULL.
inline ExecutablePool::ExecutablePool(size_t n) : m_refCount(1), m_destroy(false), m_gcNumber(0)
{
size_t allocSize = roundUpAllocationSize(n, JIT_ALLOCATOR_PAGE_SIZE);
if (allocSize == OVERSIZE_ALLOCATION) {
m_freePtr = NULL;
return;
}
#ifdef DEBUG_STRESS_JSC_ALLOCATOR
Allocation mem = systemAlloc(size_t(4294967291));
#else
Allocation mem = systemAlloc(allocSize);
#endif
if (!mem.pages) {
m_freePtr = NULL;
return;
}
if (!m_pools.append(mem)) {
systemRelease(mem);
m_freePtr = NULL;
return;
}
m_freePtr = mem.pages;
m_end = m_freePtr + allocSize;
}
inline void* ExecutablePool::poolAllocate(size_t n)
{
size_t allocSize = roundUpAllocationSize(n, JIT_ALLOCATOR_PAGE_SIZE);
if (allocSize == OVERSIZE_ALLOCATION)
return NULL;
#ifdef DEBUG_STRESS_JSC_ALLOCATOR
Allocation result = systemAlloc(size_t(4294967291));
#else
Allocation result = systemAlloc(allocSize);
#endif
if (!result.pages)
return NULL;
JS_ASSERT(m_end >= m_freePtr);
if ((allocSize - n) > static_cast<size_t>(m_end - m_freePtr)) {
// Replace allocation pool
m_freePtr = result.pages + n;
m_end = result.pages + allocSize;
}
m_pools.append(result);
return result.pages;
}
}
#endif // ENABLE(ASSEMBLER)

Просмотреть файл

@ -33,9 +33,9 @@
namespace JSC {
void ExecutableAllocator::intializePageSize()
size_t ExecutableAllocator::determinePageSize()
{
ExecutableAllocator::pageSize = 4096u;
return 4096u;
}
ExecutablePool::Allocation ExecutablePool::systemAlloc(size_t n)

Просмотреть файл

@ -33,9 +33,9 @@
namespace JSC {
void ExecutableAllocator::intializePageSize()
size_t ExecutableAllocator::determinePageSize()
{
ExecutableAllocator::pageSize = getpagesize();
return getpagesize();
}
ExecutablePool::Allocation ExecutablePool::systemAlloc(size_t n)

Просмотреть файл

@ -32,18 +32,18 @@ const size_t MOVING_MEM_PAGE_SIZE = 256 * 1024;
namespace JSC {
void ExecutableAllocator::intializePageSize()
size_t ExecutableAllocator::determinePageSize()
{
#if WTF_CPU_ARMV5_OR_LOWER
// The moving memory model (as used in ARMv5 and earlier platforms)
// on Symbian OS limits the number of chunks for each process to 16.
// To mitigate this limitation increase the pagesize to
// allocate less of larger chunks.
ExecutableAllocator::pageSize = MOVING_MEM_PAGE_SIZE;
return MOVING_MEM_PAGE_SIZE;
#else
TInt page_size;
UserHal::PageSizeInBytes(page_size);
ExecutableAllocator::pageSize = page_size;
return page_size;
#endif
}

Просмотреть файл

@ -32,11 +32,11 @@
namespace JSC {
void ExecutableAllocator::intializePageSize()
size_t ExecutableAllocator::determinePageSize()
{
SYSTEM_INFO system_info;
GetSystemInfo(&system_info);
ExecutableAllocator::pageSize = system_info.dwPageSize;
return system_info.dwPageSize;
}
ExecutablePool::Allocation ExecutablePool::systemAlloc(size_t n)

335
js/src/config/find_OOM_errors.py Executable file
Просмотреть файл

@ -0,0 +1,335 @@
#!/usr/bin/env python
usage = """%prog: A test for OOM conditions in the shell.
%prog finds segfaults and other errors caused by incorrect handling of
allocation during OOM (out-of-memory) conditions.
"""
help = """Check for regressions only. This runs a set of files with a known
number of OOM errors (specified by REGRESSION_COUNT), and exits with a non-zero
result if more or less errors are found. See js/src/Makefile.in for invocation.
"""
import hashlib
import re
import shlex
import subprocess
import sys
import threading
import time
from optparse import OptionParser
#####################################################################
# Utility functions
#####################################################################
def run(args, stdin=None):
class ThreadWorker(threading.Thread):
def __init__(self, pipe):
super(ThreadWorker, self).__init__()
self.all = ""
self.pipe = pipe
self.setDaemon(True)
def run(self):
while True:
line = self.pipe.readline()
if line == '': break
else:
self.all += line
try:
if type(args) == str:
args = shlex.split(args)
args = [str(a) for a in args] # convert to strs
stdin_pipe = subprocess.PIPE if stdin else None
proc = subprocess.Popen(args, stdin=stdin_pipe, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if stdin_pipe:
proc.stdin.write(stdin)
proc.stdin.close()
stdout_worker = ThreadWorker(proc.stdout)
stderr_worker = ThreadWorker(proc.stderr)
stdout_worker.start()
stderr_worker.start()
proc.wait()
stdout_worker.join()
stderr_worker.join()
except KeyboardInterrupt, e:
sys.exit(-1)
stdout, stderr = stdout_worker.all, stderr_worker.all
result = (stdout, stderr, proc.returncode)
return result
def get_js_files():
(out, err, exit) = run('find ../jit-test/tests -name "*.js"')
if (err, exit) == ("", 0):
sys.exit("Wrong directory, run from an objdir")
return out.split()
#####################################################################
# Blacklisting
#####################################################################
def in_blacklist(sig):
return sig in blacklist
def add_to_blacklist(sig):
blacklist[sig] = blacklist.get(sig, 0)
blacklist[sig] += 1
# How often is a particular lines important for this.
def count_lines():
"""Keep track of the amount of times individual lines occur, in order to
prioritize the errors which occur most frequently."""
counts = {}
for string,count in blacklist.items():
for line in string.split("\n"):
counts[line] = counts.get(line, 0) + count
lines = []
for k,v in counts.items():
lines.append("%6d: %s" % (v,k))
lines.sort()
countlog = file("../OOM_count_log", "w")
countlog.write("\n".join(lines))
countlog.flush()
countlog.close()
#####################################################################
# Output cleaning
#####################################################################
def clean_voutput(err):
# Skip what we can't reproduce
err = re.sub(r"^--\d+-- run: /usr/bin/dsymutil \"shell/js\"$", "", err, flags=re.MULTILINE)
err = re.sub(r"^==\d+==", "", err, flags=re.MULTILINE)
err = re.sub(r"^\*\*\d+\*\*", "", err, flags=re.MULTILINE)
err = re.sub(r"^\s+by 0x[0-9A-Fa-f]+: ", "by: ", err, flags=re.MULTILINE)
err = re.sub(r"^\s+at 0x[0-9A-Fa-f]+: ", "at: ", err, flags=re.MULTILINE)
err = re.sub(r"(^\s+Address 0x)[0-9A-Fa-f]+( is not stack'd)", r"\1\2", err, flags=re.MULTILINE)
err = re.sub(r"(^\s+Invalid write of size )\d+", r"\1x", err, flags=re.MULTILINE)
err = re.sub(r"(^\s+Invalid read of size )\d+", r"\1x", err, flags=re.MULTILINE)
err = re.sub(r"(^\s+Address 0x)[0-9A-Fa-f]+( is )\d+( bytes inside a block of size )[0-9,]+( free'd)", r"\1\2\3\4", err, flags=re.MULTILINE)
# Skip the repeating bit due to the segfault
lines = []
for l in err.split('\n'):
if l == " Process terminating with default action of signal 11 (SIGSEGV)":
break
lines.append(l)
err = '\n'.join(lines)
return err
def remove_failed_allocation_backtraces(err):
lines = []
add = True
for l in err.split('\n'):
# Set start and end conditions for including text
if l == " The site of the failed allocation is:":
add = False
elif l[:2] not in ['by: ', 'at:']:
add = True
if add:
lines.append(l)
err = '\n'.join(lines)
return err
def clean_output(err):
err = re.sub(r"^js\(\d+,0x[0-9a-f]+\) malloc: \*\*\* error for object 0x[0-9a-f]+: pointer being freed was not allocated\n\*\*\* set a breakppoint in malloc_error_break to debug\n$", "pointer being freed was not allocated", err, flags=re.MULTILINE)
return err
#####################################################################
# Consts, etc
#####################################################################
command_template = 'shell/js' \
+ ' -m -j -p' \
+ ' -e "const platform=\'darwin\'; const libdir=\'../jit-test/lib/\';"' \
+ ' -f ../jit-test/lib/prolog.js' \
+ ' -f %s'
# Blacklists are things we don't want to see in our logs again (though we do
# want to count them when they happen). Whitelists we do want to see in our
# logs again, principally because the information we have isn't enough.
blacklist = {}
add_to_blacklist(r"('', '', 1)") # 1 means OOM if the shell hasn't launched yet.
add_to_blacklist(r"('', 'out of memory\n', 1)")
whitelist = set()
whitelist.add(r"('', 'out of memory\n', -11)") # -11 means OOM
whitelist.add(r"('', 'out of memory\nout of memory\n', -11)")
#####################################################################
# Program
#####################################################################
# Options
parser = OptionParser(usage=usage)
parser.add_option("-r", "--regression", action="store", metavar="REGRESSION_COUNT", help=help,
type="int", dest="regression", default=0) # TODO: support a value of zero, eventually
(OPTIONS, args) = parser.parse_args()
if OPTIONS.regression:
# TODO: This should be expanded as we get a better hang of the OOM problems.
# For now, we'll just check that the number of OOMs in one short file does not
# increase.
files = ["../jit-test/tests/arguments/args-createontrace.js"]
else:
files = get_js_files()
# Use a command-line arg to reduce the set of files
if len (args):
files = [f for f in files if f.find(args[0]) != -1]
if OPTIONS.regression:
# Don't use a logfile, this is automated for tinderbox.
log = file("../OOM_log", "w")
num_failures = 0
for f in files:
# Run it once to establish boundaries
command = (command_template + ' -O') % (f)
out, err, exit = run(command)
max = re.match(".*OOM max count: (\d+).*", out, flags=re.DOTALL).groups()[0]
max = int(max)
# OOMs don't recover well for the first 20 allocations or so.
# TODO: revisit this.
for i in range(20, max):
if OPTIONS.regression == None:
print "Testing allocation %d/%d in %s" % (i,max,f)
command = (command_template + ' -A %d') % (f, i)
out, err, exit = run(command)
# Success (5 is SM's exit code for controlled errors)
if exit == 5 and err.find("out of memory") != -1:
continue
# Failure
else:
if OPTIONS.regression:
# Just count them
num_failures += 1
continue
#########################################################################
# The regression tests ends above. The rest of this is for running the
# script manually.
#########################################################################
problem = str((out, err, exit))
if in_blacklist(problem) and problem not in whitelist:
add_to_blacklist(problem)
continue
add_to_blacklist(problem)
# Get valgrind output for a good stack trace
vcommand = "valgrind --dsymutil=yes -q --log-file=OOM_valgrind_log_file " + command
run(vcommand)
vout = file("OOM_valgrind_log_file").read()
vout = clean_voutput(vout)
sans_alloc_sites = remove_failed_allocation_backtraces(vout)
# Don't print duplicate information
if in_blacklist(sans_alloc_sites):
add_to_blacklist(sans_alloc_sites)
continue
add_to_blacklist(sans_alloc_sites)
log.write ("\n")
log.write ("\n")
log.write ("=========================================================================")
log.write ("\n")
log.write ("An allocation failure at\n\tallocation %d/%d in %s\n\tcauses problems (detected using bug 624094)" % (i, max, f))
log.write ("\n")
log.write ("\n")
log.write ("Command (from obj directory, using patch from bug 624094):\n " + command)
log.write ("\n")
log.write ("\n")
log.write ("stdout, stderr, exitcode:\n " + problem)
log.write ("\n")
log.write ("\n")
double_free = err.find("pointer being freed was not allocated") != -1
oom_detected = err.find("out of memory") != -1
multiple_oom_detected = err.find("out of memory\nout of memory") != -1
segfault_detected = exit == -11
log.write ("Diagnosis: ")
log.write ("\n")
if multiple_oom_detected:
log.write (" - Multiple OOMs reported")
log.write ("\n")
if segfault_detected:
log.write (" - segfault")
log.write ("\n")
if not oom_detected:
log.write (" - No OOM checking")
log.write ("\n")
if double_free:
log.write (" - Double free")
log.write ("\n")
log.write ("\n")
log.write ("Valgrind info:\n" + vout)
log.write ("\n")
log.write ("\n")
log.flush()
if not OPTIONS.regression == None:
count_lines()
# Do the actual regression check
if OPTIONS.regression:
expected_num_failures = OPTIONS.regression
if num_failures != expected_num_failures:
print "TEST-UNEXPECTED-FAIL |",
if num_failures > expected_num_failures:
print "More out-of-memory errors were found (%s) than expected (%d). This probably means an allocation site has been added without a NULL-check. If this is unavoidable, you can account for it by updating Makefile.in." % (num_failures, expected_num_failures),
else:
print "Congratulations, you have removed %d out-of-memory error(s) (%d remain)! Please account for it by updating Makefile.in." % (expected_num_failures - num_failures, num_failures),
sys.exit(-1)
else:
print 'TEST-PASS | find_OOM_errors | Found the expected number of OOM errors (%d)' % (expected_num_failures)

Просмотреть файл

@ -3477,7 +3477,16 @@ case $target in
esac
dnl Performance measurement headers.
AC_CHECK_HEADER(linux/perf_event.h, HAVE_LINUX_PERF_EVENT_H=1)
AC_CHECK_HEADER(linux/perf_event.h,
[AC_CACHE_CHECK(for perf_event_open system call,ac_cv_perf_event_open,
[AC_TRY_COMPILE([#include <sys/syscall.h>],[return sizeof(__NR_perf_event_open);],
ac_cv_perf_event_open=yes,
ac_cv_perf_event_open=no)])])
if test "$ac_cv_perf_event_open" = "yes"; then
HAVE_LINUX_PERF_EVENT_H=1
else
HAVE_LINUX_PERF_EVENT_H=
fi
AC_SUBST(HAVE_LINUX_PERF_EVENT_H)
dnl Checks for libraries.

Просмотреть файл

@ -261,11 +261,11 @@ static JSClass sCDataProtoClass = {
static JSClass sCTypeClass = {
"CType",
JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS) | JSCLASS_MARK_IS_TRACE,
JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS),
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CType::Finalize,
NULL, NULL, CType::ConstructData, CType::ConstructData, NULL,
CType::HasInstance, JS_CLASS_TRACE(CType::Trace), NULL
CType::HasInstance, CType::Trace, NULL
};
static JSClass sCDataClass = {
@ -278,10 +278,10 @@ static JSClass sCDataClass = {
static JSClass sCClosureClass = {
"CClosure",
JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS) | JSCLASS_MARK_IS_TRACE,
JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS),
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CClosure::Finalize,
NULL, NULL, NULL, NULL, NULL, NULL, JS_CLASS_TRACE(CClosure::Trace), NULL
NULL, NULL, NULL, NULL, NULL, NULL, CClosure::Trace, NULL
};
#define CTYPESFN_FLAGS \
@ -4782,8 +4782,8 @@ NewFunctionInfo(JSContext* cx,
if (!ffiType)
return NULL;
fninfo->mArgTypes.append(argType);
fninfo->mFFITypes.append(ffiType);
fninfo->mArgTypes.infallibleAppend(argType);
fninfo->mFFITypes.infallibleAppend(ffiType);
}
if (fninfo->mIsVariadic)

Просмотреть файл

@ -64,7 +64,7 @@ namespace Library
static JSClass sLibraryClass = {
"Library",
JSCLASS_HAS_RESERVED_SLOTS(LIBRARY_SLOTS) | JSCLASS_MARK_IS_TRACE,
JSCLASS_HAS_RESERVED_SLOTS(LIBRARY_SLOTS),
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub,JS_ResolveStub, JS_ConvertStub, Library::Finalize,
JSCLASS_NO_OPTIONAL_MEMBERS

Просмотреть файл

@ -7,7 +7,7 @@ set title "Title goes here!"
set datafile missing "-"
set noxtics
set ytics nomirror
set ylabel "Cycles [1E6]"
set ylabel "msec"
set y2tics nomirror
set y2label "Chunk count"
set key below

Просмотреть файл

@ -1,5 +1,4 @@
for (b = 0; b < 2; b++) {
/ /
(function(){})
/ /.exec(function(){})
}

Просмотреть файл

@ -0,0 +1,10 @@
function foo() {
var name = 2;
var x = <a><name>what</name></a>;
var y = "" + x.(name != 2);
assertEq(y.match(/what/)[0], "what");
var z = "" + x.(name = 10);
assertEq(z.match(/10/)[0], "10");
}
foo();

Просмотреть файл

@ -0,0 +1,17 @@
/* Don't trip bogus assert. */
function e()
{
try
{
var t = undefined;
}
catch (e)
{
var t = null;
}
while (t && (t.tagName.toUpperCase() != "BODY"))
continue;
}
for (var i = 0; i < 20; i++)
e();

Просмотреть файл

@ -0,0 +1,8 @@
// |jit-test| error: strict
options("strict");
eval("\n\
function f(a) {\n\
if (a > 3)\n\
return 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';\n\
}");

Просмотреть файл

@ -1,9 +0,0 @@
// |jit-test| error:Error
var p = /./, x = resolver({}, p), y = resolver({lastIndex: 2}, p), v;
var a = [];
for (var i = 0; i < HOTLOOP; i++)
a[i] = x;
a[HOTLOOP] = y;
for (i = 0; i < a.length; i++)
v = a[i].lastIndex;
assertEq(v, 2); // fails due to bug 458271

Просмотреть файл

@ -0,0 +1,46 @@
function get(x) {
return x;
}
function f(x) {
switch(x) {
case get(0):
return 0;
case get(1):
return 1;
case get("foo"):
return "foo";
case get(true):
return true;
case get(false):
return false;
case get({}):
return {};
case get(null):
return null;
case Number.MIN_VALUE:
return Number.MIN_VALUE;
case Math:
return Math;
default:
return -123;
}
}
assertEq(f(0), 0);
assertEq(f(-0), 0);
assertEq(f(1), 1);
assertEq(f("foo"), "foo");
assertEq(f(true), true);
assertEq(f(false), false);
assertEq(f({}), -123);
assertEq(f([]), -123);
assertEq(f(Math), Math);
assertEq(f({x:1}), -123);
assertEq(f(333), -123);
assertEq(f(null), null);
assertEq(f(undefined), -123);
assertEq(f(Number.MIN_VALUE), Number.MIN_VALUE);

Просмотреть файл

@ -0,0 +1,33 @@
var C1 = 1;
var C2 = 2;
const C3 = 3;
function f(x) {
var s = "";
switch(x) {
case C1:
s += "1";
case C2:
s += "2";
break;
case C3:
s += "3";
default:
s += "d";
case 4:
s += "4";
}
return s;
}
assertEq(f(1), "12");
assertEq(f(2), "2");
assertEq(f(3), "3d4");
assertEq(f(4), "4");
assertEq(f(0), "d4");
assertEq(f(-0), "d4");
assertEq(f(true), "d4");

Просмотреть файл

@ -0,0 +1,22 @@
var C1 = 1;
function f(x) {
var s = "";
switch(x) {
case 0:
s += "0";
case C1:
s += "1";
}
return s;
}
assertEq(f(0), "01");
assertEq(f(1), "1");
assertEq(f(2), "");
assertEq(f(true), "");
assertEq(f(Math), "");

Просмотреть файл

@ -1,5 +1,5 @@
function testNonStubGetter() {
let ([] = false) { (this.watch("x", /a/g)); };
let ([] = false) { (this.watch("x", function(p, o, n) { return /a/g.exec(p, o, n); })); };
(function () { (eval("(function(){for each (x in [1, 2, 2]);});"))(); })();
this.unwatch("x");
return "ok";

Просмотреть файл

@ -0,0 +1,63 @@
// Test that the watch handler is not called recursively for the same object
// and property.
(function() {
var obj1 = {}, obj2 = {};
var handler_entry_count = 0;
var handler_exit_count = 0;
obj1.watch('x', handler);
obj1.watch('y', handler);
obj2.watch('x', handler);
obj1.x = 1;
assertEq(handler_entry_count, 3);
assertEq(handler_exit_count, 3);
function handler(id) {
handler_entry_count++;
assertEq(handler_exit_count, 0);
switch (true) {
case this === obj1 && id === "x":
assertEq(handler_entry_count, 1);
obj2.x = 3;
assertEq(handler_exit_count, 2);
break;
case this === obj2 && id === "x":
assertEq(handler_entry_count, 2);
obj1.y = 4;
assertEq(handler_exit_count, 1);
break;
default:
assertEq(this, obj1);
assertEq(id, "y");
assertEq(handler_entry_count, 3);
// We expect no more watch handler invocations
obj1.x = 5;
obj1.y = 6;
obj2.x = 7;
assertEq(handler_exit_count, 0);
break;
}
++handler_exit_count;
assertEq(handler_entry_count, 3);
}
})();
// Test that run-away recursion in watch handlers is properly handled.
(function() {
var obj = {};
var i = 0;
try {
handler();
throw new Error("Unreachable");
} catch(e) {
assertEq(e instanceof InternalError, true);
}
function handler() {
var prop = "a" + ++i;
obj.watch(prop, handler);
obj[prop] = 2;
}
})();

Просмотреть файл

@ -1,4 +1,4 @@
// |jit-test| error: TypeError
__defineGetter__("x",/a/)
" ".replace(/\s/,"")
__defineGetter__("x", function() { return /a/.exec(undefined); } );
" ".replace(/\s/,"");
x.b

Просмотреть файл

@ -1,4 +1,4 @@
this.__defineSetter__("x",/a/)
this.__defineSetter__("x", function(v) { return /a/.exec(v); })
Function("\
for each(w in[0,0,0]) {\
for each(y in[0,0,0,0,0,0,0,0,x,0,0,0,0,0,0,0,0,0,x,0,0,0,0,0,0,0,x]) {}\

Просмотреть файл

@ -422,8 +422,8 @@ Script::analyze(JSContext *cx, JSScript *script)
if (locals[local] == LOCAL_CONDITIONALLY_DEFINED)
setLocal(local, offset);
}
defineArray = NULL;
defineCount = 0;
defineArray = bytecode->defineArray = NULL;
defineCount = bytecode->defineCount = 0;
}
unsigned nuses, ndefs;

Просмотреть файл

@ -67,6 +67,7 @@ CPPSRCS = \
testNewObject.cpp \
testOps.cpp \
testPropCache.cpp \
testResolveRecursion.cpp \
testSameValue.cpp \
testScriptObject.cpp \
testSetProperty.cpp \

Просмотреть файл

@ -0,0 +1,134 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=99:
*/
#include "tests.h"
/*
* Test that resolve hook recursion for the same object and property is
* prevented.
*/
BEGIN_TEST(testResolveRecursion)
{
static JSClass my_resolve_class = {
"MyResolve",
JSCLASS_NEW_RESOLVE | JSCLASS_HAS_PRIVATE,
JS_PropertyStub, // add
JS_PropertyStub, // delete
JS_PropertyStub, // get
JS_StrictPropertyStub, // set
JS_EnumerateStub,
(JSResolveOp) my_resolve,
JS_ConvertStub,
JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS
};
obj1 = JS_NewObject(cx, &my_resolve_class, NULL, NULL);
CHECK(obj1);
obj2 = JS_NewObject(cx, &my_resolve_class, NULL, NULL);
CHECK(obj2);
CHECK(JS_SetPrivate(cx, obj1, this));
CHECK(JS_SetPrivate(cx, obj2, this));
CHECK(JS_DefineProperty(cx, global, "obj1", OBJECT_TO_JSVAL(obj1), NULL, NULL, 0));
CHECK(JS_DefineProperty(cx, global, "obj2", OBJECT_TO_JSVAL(obj2), NULL, NULL, 0));
resolveEntryCount = 0;
resolveExitCount = 0;
/* Start the essence of the test via invoking the first resolve hook. */
jsval v;
EVAL("obj1.x", &v);
CHECK(v == JSVAL_FALSE);
CHECK(resolveEntryCount == 4);
CHECK(resolveExitCount == 4);
return true;
}
JSObject *obj1;
JSObject *obj2;
unsigned resolveEntryCount;
unsigned resolveExitCount;
struct AutoIncrCounters {
AutoIncrCounters(cls_testResolveRecursion *t) : t(t) {
t->resolveEntryCount++;
}
~AutoIncrCounters() {
t->resolveExitCount++;
}
cls_testResolveRecursion *t;
};
bool
doResolve(JSObject *obj, jsid id, uintN flags, JSObject **objp)
{
CHECK(resolveExitCount == 0);
AutoIncrCounters incr(this);
CHECK(obj == obj1 || obj == obj2);
CHECK(JSID_IS_STRING(id));
JSFlatString *str = JS_FlattenString(cx, JSID_TO_STRING(id));
CHECK(str);
jsval v;
if (JS_FlatStringEqualsAscii(str, "x")) {
if (obj == obj1) {
/* First resolve hook invocation. */
CHECK(resolveEntryCount == 1);
EVAL("obj2.y = true", &v);
CHECK(v == JSVAL_TRUE);
CHECK(JS_DefinePropertyById(cx, obj, id, JSVAL_FALSE, NULL, NULL, 0));
*objp = obj;
return true;
}
if (obj == obj2) {
CHECK(resolveEntryCount == 4);
*objp = NULL;
return true;
}
} else if (JS_FlatStringEqualsAscii(str, "y")) {
if (obj == obj2) {
CHECK(resolveEntryCount == 2);
CHECK(JS_DefinePropertyById(cx, obj, id, JSVAL_NULL, NULL, NULL, 0));
EVAL("obj1.x", &v);
CHECK(JSVAL_IS_VOID(v));
EVAL("obj1.y", &v);
CHECK(v == JSVAL_ZERO);
*objp = obj;
return true;
}
if (obj == obj1) {
CHECK(resolveEntryCount == 3);
EVAL("obj1.x", &v);
CHECK(JSVAL_IS_VOID(v));
EVAL("obj1.y", &v);
CHECK(JSVAL_IS_VOID(v));
EVAL("obj2.y", &v);
CHECK(JSVAL_IS_NULL(v));
EVAL("obj2.x", &v);
CHECK(JSVAL_IS_VOID(v));
EVAL("obj1.y = 0", &v);
CHECK(v == JSVAL_ZERO);
*objp = obj;
return true;
}
}
CHECK(false);
return false;
}
static JSBool
my_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp)
{
return static_cast<cls_testResolveRecursion *>(JS_GetPrivate(cx, obj))->
doResolve(obj, id, flags, objp);
}
END_TEST(testResolveRecursion)

Просмотреть файл

@ -15,31 +15,15 @@ struct ScriptObjectFixture : public JSAPITest {
uc_code[i] = code[i];
}
bool tryScript(JSScript *script)
bool tryScript(JSObject *scriptObj)
{
CHECK(script);
/* We should have allocated a script object for the script already. */
jsvalRoot script_object(cx, OBJECT_TO_JSVAL(JS_GetScriptObject(script)));
CHECK(JSVAL_TO_OBJECT(script_object.value()));
/*
* JS_NewScriptObject just returns the object already created for the
* script. It was always a bug to call this more than once.
*/
jsvalRoot second_script_object
(cx, OBJECT_TO_JSVAL(JS_NewScriptObject(cx, script)));
CHECK_SAME(second_script_object.value(), script_object.value());
CHECK(scriptObj);
JS_GC(cx);
/* After a garbage collection, the script should still work. */
jsval result;
CHECK(JS_ExecuteScript(cx, global, script, &result));
/* JS_DestroyScript must still be safe to call, whether or not it
actually has any effect. */
JS_DestroyScript(cx, script);
CHECK(JS_ExecuteScript(cx, global, scriptObj, &result));
return true;
}
@ -103,9 +87,9 @@ BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFile)
FILE *script_stream = tempScript.open(script_filename);
CHECK(fputs(code, script_stream) != EOF);
tempScript.close();
JSScript *script = JS_CompileFile(cx, global, script_filename);
JSObject *scriptObj = JS_CompileFile(cx, global, script_filename);
tempScript.remove();
return tryScript(script);
return tryScript(scriptObj);
}
END_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFile)
@ -115,9 +99,9 @@ BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFile_empty)
static const char script_filename[] = "temp-bug438633_JS_CompileFile_empty";
tempScript.open(script_filename);
tempScript.close();
JSScript *script = JS_CompileFile(cx, global, script_filename);
JSObject *scriptObj = JS_CompileFile(cx, global, script_filename);
tempScript.remove();
return tryScript(script);
return tryScript(scriptObj);
}
END_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFile_empty)
@ -152,19 +136,3 @@ BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFileHandleForPrincip
script_stream, NULL));
}
END_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFileHandleForPrincipals)
BEGIN_TEST(testScriptObject_ScriptlessScriptObjects)
{
/* JS_NewScriptObject(cx, NULL) should return a fresh object each time. */
jsvalRoot script_object1(cx, OBJECT_TO_JSVAL(JS_NewScriptObject(cx, NULL)));
CHECK(!JSVAL_IS_PRIMITIVE(script_object1.value()));
jsvalRoot script_object2(cx, OBJECT_TO_JSVAL(JS_NewScriptObject(cx, NULL)));
CHECK(!JSVAL_IS_PRIMITIVE(script_object2.value()));
if (script_object1.value() == script_object2.value())
return false;
return true;
}
END_TEST(testScriptObject_ScriptlessScriptObjects)

Просмотреть файл

@ -30,15 +30,12 @@ BEGIN_TEST(testTrap_gc)
;
// compile
JSScript *script = JS_CompileScript(cx, global, source, strlen(source), __FILE__, 1);
CHECK(script);
JSObject *scrobj = JS_NewScriptObject(cx, script);
CHECK(scrobj);
jsvalRoot v(cx, OBJECT_TO_JSVAL(scrobj));
JSObject *scriptObj = JS_CompileScript(cx, global, source, strlen(source), __FILE__, 1);
CHECK(scriptObj);
// execute
jsvalRoot v2(cx);
CHECK(JS_ExecuteScript(cx, global, script, v2.addr()));
CHECK(JS_ExecuteScript(cx, global, scriptObj, v2.addr()));
CHECK(JSVAL_IS_OBJECT(v2));
CHECK(emptyTrapCallCount == 0);
@ -48,24 +45,31 @@ BEGIN_TEST(testTrap_gc)
// Enable debug mode
CHECK(JS_SetDebugMode(cx, JS_TRUE));
jsbytecode *line2 = JS_LineNumberToPC(cx, script, 1);
CHECK(line2);
jsbytecode *line6 = JS_LineNumberToPC(cx, script, 5);
CHECK(line2);
static const char trapClosureText[] = "some trap closure";
JSString *trapClosure = JS_NewStringCopyZ(cx, trapClosureText);
CHECK(trapClosure);
JS_SetTrap(cx, script, line2, EmptyTrapHandler, STRING_TO_JSVAL(trapClosure));
JS_SetTrap(cx, script, line6, EmptyTrapHandler, STRING_TO_JSVAL(trapClosure));
JS_GC(cx);
CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(trapClosure), trapClosureText));
// scope JSScript usage to make sure that it is not used after
// JS_ExecuteScript. This way we avoid using Anchor.
JSString *trapClosure;
{
JSScript *script = JS_GetScriptFromObject(scriptObj);
jsbytecode *line2 = JS_LineNumberToPC(cx, script, 1);
CHECK(line2);
jsbytecode *line6 = JS_LineNumberToPC(cx, script, 5);
CHECK(line2);
trapClosure = JS_NewStringCopyZ(cx, trapClosureText);
CHECK(trapClosure);
JS_SetTrap(cx, script, line2, EmptyTrapHandler, STRING_TO_JSVAL(trapClosure));
JS_SetTrap(cx, script, line6, EmptyTrapHandler, STRING_TO_JSVAL(trapClosure));
JS_GC(cx);
CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(trapClosure), trapClosureText));
}
// execute
CHECK(JS_ExecuteScript(cx, global, script, v2.addr()));
CHECK(JS_ExecuteScript(cx, global, scriptObj, v2.addr()));
CHECK(emptyTrapCallCount == 11);
JS_GC(cx);

Просмотреть файл

@ -41,7 +41,7 @@ struct VersionFixture : public JSAPITest
EvalScriptVersion16, 0, 0);
}
JSScript *fakeScript(const char *contents, size_t length) {
JSObject *fakeScript(const char *contents, size_t length) {
return JS_CompileScript(cx, global, contents, length, "<test>", 1);
}
@ -74,9 +74,9 @@ struct VersionFixture : public JSAPITest
/* Check that script compilation results in a version without XML. */
bool checkNewScriptNoXML() {
JSScript *script = fakeScript("", 0);
CHECK(script);
CHECK(!hasXML(script->getVersion()));
JSObject *scriptObj = fakeScript("", 0);
CHECK(scriptObj);
CHECK(!hasXML(JS_GetScriptFromObject(scriptObj)->getVersion()));
return true;
}
@ -195,15 +195,12 @@ BEGIN_FIXTURE_TEST(VersionFixture, testOptionsAreUsedForVersionFlags)
"disableXMLOption();"
"callSetVersion17();"
"checkNewScriptNoXML();";
JSScript *toActivate = fakeScript(toActivateChars, sizeof(toActivateChars) - 1);
JSObject *toActivate = fakeScript(toActivateChars, sizeof(toActivateChars) - 1);
CHECK(toActivate);
JSObject *scriptObject = JS_GetScriptObject(toActivate);
CHECK(hasXML(toActivate));
CHECK(hasXML(JS_GetScriptFromObject(toActivate)));
disableXML();
CHECK(scriptObject);
/* Activate the script. */
jsval dummy;
CHECK(JS_ExecuteScript(cx, global, toActivate, &dummy));

Просмотреть файл

@ -16,16 +16,13 @@ BEGIN_TEST(testXDR_bug506491)
"var f = makeClosure('0;', 'status', 'ok');\n";
// compile
JSScript *script = JS_CompileScript(cx, global, s, strlen(s), __FILE__, __LINE__);
CHECK(script);
JSObject *scrobj = JS_NewScriptObject(cx, script);
CHECK(scrobj);
jsvalRoot v(cx, OBJECT_TO_JSVAL(scrobj));
JSObject *scriptObj = JS_CompileScript(cx, global, s, strlen(s), __FILE__, __LINE__);
CHECK(scriptObj);
// freeze
JSXDRState *w = JS_XDRNewMem(cx, JSXDR_ENCODE);
CHECK(w);
CHECK(JS_XDRScript(w, &script));
CHECK(JS_XDRScriptObject(w, &scriptObj));
uint32 nbytes;
void *p = JS_XDRMemGetData(w, &nbytes);
CHECK(p);
@ -35,18 +32,15 @@ BEGIN_TEST(testXDR_bug506491)
JS_XDRDestroy(w);
// thaw
script = NULL;
scriptObj = NULL;
JSXDRState *r = JS_XDRNewMem(cx, JSXDR_DECODE);
JS_XDRMemSetData(r, frozen, nbytes);
CHECK(JS_XDRScript(r, &script));
CHECK(JS_XDRScriptObject(r, &scriptObj));
JS_XDRDestroy(r); // this frees `frozen`
scrobj = JS_NewScriptObject(cx, script);
CHECK(scrobj);
v = OBJECT_TO_JSVAL(scrobj);
// execute
jsvalRoot v2(cx);
CHECK(JS_ExecuteScript(cx, global, script, v2.addr()));
CHECK(JS_ExecuteScript(cx, global, scriptObj, v2.addr()));
// try to break the Block object that is the parent of f
JS_GC(cx);
@ -62,16 +56,13 @@ END_TEST(testXDR_bug506491)
BEGIN_TEST(testXDR_bug516827)
{
// compile an empty script
JSScript *script = JS_CompileScript(cx, global, "", 0, __FILE__, __LINE__);
CHECK(script);
JSObject *scrobj = JS_NewScriptObject(cx, script);
CHECK(scrobj);
jsvalRoot v(cx, OBJECT_TO_JSVAL(scrobj));
JSObject *scriptObj = JS_CompileScript(cx, global, "", 0, __FILE__, __LINE__);
CHECK(scriptObj);
// freeze
JSXDRState *w = JS_XDRNewMem(cx, JSXDR_ENCODE);
CHECK(w);
CHECK(JS_XDRScript(w, &script));
CHECK(JS_XDRScriptObject(w, &scriptObj));
uint32 nbytes;
void *p = JS_XDRMemGetData(w, &nbytes);
CHECK(p);
@ -81,17 +72,14 @@ BEGIN_TEST(testXDR_bug516827)
JS_XDRDestroy(w);
// thaw
script = NULL;
scriptObj = NULL;
JSXDRState *r = JS_XDRNewMem(cx, JSXDR_DECODE);
JS_XDRMemSetData(r, frozen, nbytes);
CHECK(JS_XDRScript(r, &script));
CHECK(JS_XDRScriptObject(r, &scriptObj));
JS_XDRDestroy(r); // this frees `frozen`
scrobj = JS_NewScriptObject(cx, script);
CHECK(scrobj);
v = OBJECT_TO_JSVAL(scrobj);
// execute with null result meaning no result wanted
CHECK(JS_ExecuteScript(cx, global, script, NULL));
CHECK(JS_ExecuteScript(cx, global, scriptObj, NULL));
return true;
}
END_TEST(testXDR_bug516827)

Просмотреть файл

@ -77,12 +77,10 @@
#include "jsparse.h"
#include "jsproxy.h"
#include "jsregexp.h"
#include "jsscan.h"
#include "jsscope.h"
#include "jsscript.h"
#include "jsstr.h"
#include "jstracer.h"
#include "jsdbgapi.h"
#include "prmjtime.h"
#include "jsstaticcheck.h"
#include "jsvector.h"
@ -112,16 +110,6 @@
using namespace js;
using namespace js::gc;
static JSClass dummy_class = {
"jdummy",
JSCLASS_GLOBAL_FLAGS,
JS_PropertyStub, JS_PropertyStub,
JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub, NULL,
JSCLASS_NO_OPTIONAL_MEMBERS
};
/*
* This class is a version-establising barrier at the head of a VM entry or
* re-entry. It ensures that:
@ -611,6 +599,18 @@ JS_SameValue(JSContext *cx, jsval v1, jsval v2, JSBool *same)
return SameValue(cx, Valueify(v1), Valueify(v2), same);
}
JS_PUBLIC_API(JSBool)
JS_IsBuiltinEvalFunction(JSFunction *fun)
{
return IsAnyBuiltinEval(fun);
}
JS_PUBLIC_API(JSBool)
JS_IsBuiltinFunctionConstructor(JSFunction *fun)
{
return IsBuiltinFunctionConstructor(fun);
}
/************************************************************************/
/*
@ -1192,6 +1192,16 @@ JS_EnterCrossCompartmentCall(JSContext *cx, JSObject *target)
JS_PUBLIC_API(JSCrossCompartmentCall *)
JS_EnterCrossCompartmentCallScript(JSContext *cx, JSScript *target)
{
static JSClass dummy_class = {
"jdummy",
JSCLASS_GLOBAL_FLAGS,
JS_PropertyStub, JS_PropertyStub,
JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub, NULL,
JSCLASS_NO_OPTIONAL_MEMBERS
};
CHECK_REQUEST(cx);
JS_ASSERT(target);
@ -1226,8 +1236,16 @@ JSAutoEnterCompartment::enter(JSContext *cx, JSObject *target)
return call != NULL;
}
void
JSAutoEnterCompartment::enterAndIgnoreErrors(JSContext *cx, JSObject *target)
{
(void) enter(cx, target);
}
namespace JS {
bool
JSAutoEnterCompartment::enter(JSContext *cx, JSScript *target)
AutoEnterScriptCompartment::enter(JSContext *cx, JSScript *target)
{
JS_ASSERT(!call);
if (cx->compartment == target->compartment) {
@ -1238,11 +1256,7 @@ JSAutoEnterCompartment::enter(JSContext *cx, JSScript *target)
return call != NULL;
}
void
JSAutoEnterCompartment::enterAndIgnoreErrors(JSContext *cx, JSObject *target)
{
(void) enter(cx, target);
}
} /* namespace JS */
JS_PUBLIC_API(void *)
JS_SetCompartmentPrivate(JSContext *cx, JSCompartment *compartment, void *data)
@ -1319,15 +1333,16 @@ JS_TransplantObject(JSContext *cx, JSObject *origobj, JSObject *target)
// types of security wrappers based on whether the new compartment
// is same origin with them.
Value targetv = ObjectValue(*obj);
WrapperVector &vector = cx->runtime->compartments;
CompartmentVector &vector = cx->runtime->compartments;
AutoValueVector toTransplant(cx);
toTransplant.reserve(vector.length());
if (!toTransplant.reserve(vector.length()))
return NULL;
for (JSCompartment **p = vector.begin(), **end = vector.end(); p != end; ++p) {
WrapperMap &pmap = (*p)->crossCompartmentWrappers;
if (WrapperMap::Ptr wp = pmap.lookup(origv)) {
// We found a wrapper. Remember and root it.
toTransplant.append(wp->value);
toTransplant.infallibleAppend(wp->value);
}
}
@ -1412,15 +1427,16 @@ js_TransplantObjectWithWrapper(JSContext *cx,
// and not |origwrapper|. They need to be updated to point at the new
// location object.
Value targetv = ObjectValue(*targetobj);
WrapperVector &vector = cx->runtime->compartments;
CompartmentVector &vector = cx->runtime->compartments;
AutoValueVector toTransplant(cx);
toTransplant.reserve(vector.length());
if (!toTransplant.reserve(vector.length()))
return NULL;
for (JSCompartment **p = vector.begin(), **end = vector.end(); p != end; ++p) {
WrapperMap &pmap = (*p)->crossCompartmentWrappers;
if (WrapperMap::Ptr wp = pmap.lookup(origv)) {
// We found a wrapper. Remember and root it.
toTransplant.append(wp->value);
toTransplant.infallibleAppend(wp->value);
}
}
@ -1482,37 +1498,6 @@ JS_SetGlobalObject(JSContext *cx, JSObject *obj)
cx->resetCompartment();
}
class AutoResolvingEntry {
public:
AutoResolvingEntry() : entry(NULL) {}
/*
* Returns false on error. But N.B. if obj[id] was already being resolved,
* this is a no-op, and we silently treat that as success.
*/
bool start(JSContext *cx, JSObject *obj, jsid id, uint32 flag) {
JS_ASSERT(!entry);
this->cx = cx;
key.obj = obj;
key.id = id;
this->flag = flag;
bool ok = !!js_StartResolving(cx, &key, flag, &entry);
JS_ASSERT_IF(!ok, !entry);
return ok;
}
~AutoResolvingEntry() {
if (entry)
js_StopResolving(cx, &key, flag, NULL, 0);
}
private:
JSContext *cx;
JSResolvingKey key;
uint32 flag;
JSResolvingEntry *entry;
};
JSObject *
js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj)
{
@ -1523,13 +1508,10 @@ js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj)
if (!cx->globalObject)
JS_SetGlobalObject(cx, obj);
/* Record Function and Object in cx->resolvingTable. */
AutoResolvingEntry e1, e2;
/* Record Function and Object in cx->resolvingList. */
JSAtom **classAtoms = cx->runtime->atomState.classAtoms;
if (!e1.start(cx, obj, ATOM_TO_JSID(classAtoms[JSProto_Function]), JSRESFLAG_LOOKUP) ||
!e2.start(cx, obj, ATOM_TO_JSID(classAtoms[JSProto_Object]), JSRESFLAG_LOOKUP)) {
return NULL;
}
AutoResolving resolving1(cx, obj, ATOM_TO_JSID(classAtoms[JSProto_Function]));
AutoResolving resolving2(cx, obj, ATOM_TO_JSID(classAtoms[JSProto_Object]));
/* Initialize the function class first so constructors can be made. */
if (!js_GetClassPrototype(cx, obj, JSProto_Function, &fun_proto))
@ -2186,6 +2168,11 @@ JS_RemoveGCThingRoot(JSContext *cx, void **rp)
return js_RemoveRoot(cx->runtime, (void *)rp);
}
JS_NEVER_INLINE JS_PUBLIC_API(void)
JS_AnchorPtr(void *p)
{
}
#ifdef DEBUG
JS_PUBLIC_API(void)
@ -2628,23 +2615,6 @@ JS_DumpHeap(JSContext *cx, FILE *fp, void* startThing, uint32 startKind,
#endif /* DEBUG */
JS_PUBLIC_API(void)
JS_MarkGCThing(JSContext *cx, jsval v, const char *name, void *arg)
{
JSTracer *trc;
trc = (JSTracer *)arg;
if (!trc)
trc = cx->runtime->gcMarkingTracer;
else
JS_ASSERT(trc == cx->runtime->gcMarkingTracer);
#ifdef JS_THREADSAFE
JS_ASSERT(cx->runtime->gcThread == trc->context->thread);
#endif
MarkValue(trc, Valueify(v), name ? name : "unknown");
}
extern JS_PUBLIC_API(JSBool)
JS_IsGCMarkingTracer(JSTracer *trc)
{
@ -2804,20 +2774,6 @@ JS_NewExternalString(JSContext *cx, jschar *chars, size_t length, intN type)
return str;
}
JS_PUBLIC_API(intN)
JS_GetExternalStringGCType(JSRuntime *rt, JSString *str)
{
/*
* No need to test this in js_GetExternalStringGCType, which asserts its
* inverse instead of wasting cycles on testing a condition we can ensure
* by auditing in-VM calls to the js_... helper.
*/
if (JSString::isStatic(str))
return -1;
return js_GetExternalStringGCType(str);
}
JS_PUBLIC_API(void)
JS_SetThreadStackLimit(JSContext *cx, jsuword limitAddr)
{
@ -3077,6 +3033,23 @@ JS_NewGlobalObject(JSContext *cx, JSClass *clasp)
return obj;
}
class AutoHoldCompartment {
public:
explicit AutoHoldCompartment(JSCompartment *compartment JS_GUARD_OBJECT_NOTIFIER_PARAM)
: holdp(&compartment->hold)
{
JS_GUARD_OBJECT_NOTIFIER_INIT;
*holdp = true;
}
~AutoHoldCompartment() {
*holdp = false;
}
private:
bool *holdp;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
};
JS_PUBLIC_API(JSObject *)
JS_NewCompartmentAndGlobalObject(JSContext *cx, JSClass *clasp, JSPrincipals *principals)
{
@ -3085,6 +3058,8 @@ JS_NewCompartmentAndGlobalObject(JSContext *cx, JSClass *clasp, JSPrincipals *pr
if (!compartment)
return NULL;
AutoHoldCompartment hold(compartment);
JSCompartment *saved = cx->compartment;
cx->compartment = compartment;
JSObject *obj = JS_NewGlobalObject(cx, clasp);
@ -4025,8 +4000,7 @@ prop_iter_trace(JSTracer *trc, JSObject *obj)
static Class prop_iter_class = {
"PropertyIterator",
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) |
JSCLASS_MARK_IS_TRACE,
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1),
PropertyStub, /* addProperty */
PropertyStub, /* delProperty */
PropertyStub, /* getProperty */
@ -4041,7 +4015,7 @@ static Class prop_iter_class = {
NULL, /* construct */
NULL, /* xdrObject */
NULL, /* hasInstance */
JS_CLASS_TRACE(prop_iter_trace)
prop_iter_trace
};
JS_PUBLIC_API(JSObject *)
@ -4515,7 +4489,7 @@ JS_OPTIONS_TO_TCFLAGS(JSContext *cx)
(cx->hasRunOption(JSOPTION_NO_SCRIPT_RVAL) ? TCF_NO_SCRIPT_RVAL : 0);
}
static JSScript *
static JSObject *
CompileUCScriptForPrincipalsCommon(JSContext *cx, JSObject *obj, JSPrincipals *principals,
const jschar *chars, size_t length,
const char *filename, uintN lineno, JSVersion version)
@ -4527,15 +4501,17 @@ CompileUCScriptForPrincipalsCommon(JSContext *cx, JSObject *obj, JSPrincipals *p
uint32 tcflags = JS_OPTIONS_TO_TCFLAGS(cx) | TCF_NEED_MUTABLE_SCRIPT;
JSScript *script = Compiler::compileScript(cx, obj, NULL, principals, tcflags,
chars, length, filename, lineno, version);
if (script && !js_NewScriptObject(cx, script)) {
js_DestroyScript(cx, script);
script = NULL;
JSObject *scriptObj = NULL;
if (script) {
scriptObj = js_NewScriptObject(cx, script);
if (!scriptObj)
js_DestroyScript(cx, script);
}
LAST_FRAME_CHECKS(cx, script);
return script;
LAST_FRAME_CHECKS(cx, scriptObj);
return scriptObj;
}
extern JS_PUBLIC_API(JSScript *)
extern JS_PUBLIC_API(JSObject *)
JS_CompileUCScriptForPrincipalsVersion(JSContext *cx, JSObject *obj,
JSPrincipals *principals,
const jschar *chars, size_t length,
@ -4547,7 +4523,7 @@ JS_CompileUCScriptForPrincipalsVersion(JSContext *cx, JSObject *obj,
avi.version());
}
JS_PUBLIC_API(JSScript *)
JS_PUBLIC_API(JSObject *)
JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *principals,
const jschar *chars, size_t length,
const char *filename, uintN lineno)
@ -4556,7 +4532,7 @@ JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *prin
cx->findVersion());
}
JS_PUBLIC_API(JSScript *)
JS_PUBLIC_API(JSObject *)
JS_CompileUCScript(JSContext *cx, JSObject *obj, const jschar *chars, size_t length,
const char *filename, uintN lineno)
{
@ -4564,7 +4540,7 @@ JS_CompileUCScript(JSContext *cx, JSObject *obj, const jschar *chars, size_t len
return JS_CompileUCScriptForPrincipals(cx, obj, NULL, chars, length, filename, lineno);
}
JS_PUBLIC_API(JSScript *)
JS_PUBLIC_API(JSObject *)
JS_CompileScriptForPrincipalsVersion(JSContext *cx, JSObject *obj,
JSPrincipals *principals,
const char *bytes, size_t length,
@ -4575,7 +4551,7 @@ JS_CompileScriptForPrincipalsVersion(JSContext *cx, JSObject *obj,
return JS_CompileScriptForPrincipals(cx, obj, principals, bytes, length, filename, lineno);
}
JS_PUBLIC_API(JSScript *)
JS_PUBLIC_API(JSObject *)
JS_CompileScriptForPrincipals(JSContext *cx, JSObject *obj,
JSPrincipals *principals,
const char *bytes, size_t length,
@ -4587,12 +4563,13 @@ JS_CompileScriptForPrincipals(JSContext *cx, JSObject *obj,
jschar *chars = js_InflateString(cx, bytes, &length);
if (!chars)
return NULL;
JSScript *script = JS_CompileUCScriptForPrincipals(cx, obj, principals, chars, length, filename, lineno);
JSObject *scriptObj =
JS_CompileUCScriptForPrincipals(cx, obj, principals, chars, length, filename, lineno);
cx->free(chars);
return script;
return scriptObj;
}
JS_PUBLIC_API(JSScript *)
JS_PUBLIC_API(JSObject *)
JS_CompileScript(JSContext *cx, JSObject *obj, const char *bytes, size_t length,
const char *filename, uintN lineno)
{
@ -4650,8 +4627,8 @@ JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj, const char *bytes, size_
# define fast_getc getc
#endif
static JSScript *
CompileFileHelper(JSContext *cx, JSObject *obj, JSPrincipals *principals, uint32 tcflags,
static JSObject *
CompileFileHelper(JSContext *cx, JSObject *obj, JSPrincipals *principals,
const char* filename, FILE *fp)
{
struct stat st;
@ -4672,7 +4649,7 @@ CompileFileHelper(JSContext *cx, JSObject *obj, JSPrincipals *principals, uint32
bool hitEOF = false;
while (!hitEOF) {
len *= 2;
jschar* tmpbuf = (jschar *) cx->realloc(buf, len * sizeof(jschar));
jschar* tmpbuf = (jschar *) js_realloc(buf, len * sizeof(jschar));
if (!tmpbuf) {
cx->free(buf);
return NULL;
@ -4689,7 +4666,7 @@ CompileFileHelper(JSContext *cx, JSObject *obj, JSPrincipals *principals, uint32
}
}
} else {
buf = (jschar *) cx->malloc(len * sizeof(jschar));
buf = (jschar *) js_malloc(len * sizeof(jschar));
if (!buf)
return NULL;
@ -4700,68 +4677,64 @@ CompileFileHelper(JSContext *cx, JSObject *obj, JSPrincipals *principals, uint32
JS_ASSERT(i <= len);
len = i;
uint32 tcflags = JS_OPTIONS_TO_TCFLAGS(cx) | TCF_NEED_MUTABLE_SCRIPT;
script = Compiler::compileScript(cx, obj, NULL, principals, tcflags, buf, len, filename, 1,
cx->findVersion());
cx->free(buf);
return script;
js_free(buf);
if (!script)
return NULL;
JSObject *scriptObj = js_NewScriptObject(cx, script);
if (!scriptObj)
js_DestroyScript(cx, script);
return scriptObj;
}
JS_PUBLIC_API(JSScript *)
JS_PUBLIC_API(JSObject *)
JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename)
{
JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
FILE *fp;
uint32 tcflags;
JSScript *script;
CHECK_REQUEST(cx);
assertSameCompartment(cx, obj);
if (!filename || strcmp(filename, "-") == 0) {
fp = stdin;
} else {
fp = fopen(filename, "r");
if (!fp) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_OPEN,
filename, "No such file or directory");
return NULL;
JSObject *scriptObj = NULL;
do {
FILE *fp;
if (!filename || strcmp(filename, "-") == 0) {
fp = stdin;
} else {
fp = fopen(filename, "r");
if (!fp) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_OPEN,
filename, "No such file or directory");
break;
}
}
}
tcflags = JS_OPTIONS_TO_TCFLAGS(cx) | TCF_NEED_MUTABLE_SCRIPT;
script = CompileFileHelper(cx, obj, NULL, tcflags, filename, fp);
if (fp != stdin)
fclose(fp);
if (script && !js_NewScriptObject(cx, script)) {
js_DestroyScript(cx, script);
script = NULL;
}
LAST_FRAME_CHECKS(cx, script);
return script;
scriptObj = CompileFileHelper(cx, obj, NULL, filename, fp);
if (fp != stdin)
fclose(fp);
} while (false);
LAST_FRAME_CHECKS(cx, scriptObj);
return scriptObj;
}
JS_PUBLIC_API(JSScript *)
JS_CompileFileHandleForPrincipals(JSContext *cx, JSObject *obj, const char *filename, FILE *file,
JSPrincipals *principals)
JS_PUBLIC_API(JSObject *)
JS_CompileFileHandleForPrincipals(JSContext *cx, JSObject *obj, const char *filename,
FILE *file, JSPrincipals *principals)
{
JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
uint32 tcflags;
JSScript *script;
CHECK_REQUEST(cx);
assertSameCompartment(cx, obj, principals);
tcflags = JS_OPTIONS_TO_TCFLAGS(cx) | TCF_NEED_MUTABLE_SCRIPT;
script = CompileFileHelper(cx, obj, principals, tcflags, filename, file);
if (script && !js_NewScriptObject(cx, script)) {
js_DestroyScript(cx, script);
script = NULL;
}
LAST_FRAME_CHECKS(cx, script);
return script;
JSObject *scriptObj = CompileFileHelper(cx, obj, principals, filename, file);
LAST_FRAME_CHECKS(cx, scriptObj);
return scriptObj;
}
JS_PUBLIC_API(JSScript *)
JS_PUBLIC_API(JSObject *)
JS_CompileFileHandleForPrincipalsVersion(JSContext *cx, JSObject *obj, const char *filename,
FILE *file, JSPrincipals *principals, JSVersion version)
{
@ -4769,58 +4742,19 @@ JS_CompileFileHandleForPrincipalsVersion(JSContext *cx, JSObject *obj, const cha
return JS_CompileFileHandleForPrincipals(cx, obj, filename, file, principals);
}
JS_PUBLIC_API(JSScript *)
JS_PUBLIC_API(JSObject *)
JS_CompileFileHandle(JSContext *cx, JSObject *obj, const char *filename, FILE *file)
{
JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
return JS_CompileFileHandleForPrincipals(cx, obj, filename, file, NULL);
}
JS_PUBLIC_API(JSObject *)
JS_NewScriptObject(JSContext *cx, JSScript *script)
JS_PUBLIC_API(JSScript *)
JS_GetScriptFromObject(JSObject *scriptObj)
{
JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
CHECK_REQUEST(cx);
assertSameCompartment(cx, script);
if (!script)
return NewNonFunction<WithProto::Class>(cx, &js_ScriptClass, NULL, NULL);
JS_ASSERT(scriptObj->isScript());
/*
* This function should only ever be applied to JSScripts that had
* script objects allocated for them when they were created, as
* described in the comment for JSScript::u.object.
*/
JS_ASSERT(script->u.object);
return script->u.object;
}
JS_PUBLIC_API(JSObject *)
JS_GetScriptObject(JSScript *script)
{
/*
* This function should only ever be applied to JSScripts that had
* script objects allocated for them when they were created, as
* described in the comment for JSScript::u.object.
*/
JS_ASSERT(script->u.object);
return script->u.object;
}
JS_PUBLIC_API(void)
JS_DestroyScript(JSContext *cx, JSScript *script)
{
CHECK_REQUEST(cx);
/*
* Originally, JSScript lifetimes were managed explicitly, and this function
* was used to free a JSScript. Now, this function does nothing, and the
* garbage collector manages JSScripts; you must root the JSScript's script
* object (obtained via JS_GetScriptObject) to keep it alive.
*
* However, since the script objects have taken over this responsibility, it
* follows that every script passed here must have a script object.
*/
JS_ASSERT(script->u.object);
return (JSScript *) scriptObj->getPrivate();
}
static JSFunction *
@ -4852,10 +4786,17 @@ CompileUCFunctionForPrincipalsCommon(JSContext *cx, JSObject *obj,
goto out2;
{
EmptyShape *emptyCallShape = EmptyShape::getEmptyCallShape(cx);
if (!emptyCallShape) {
fun = NULL;
goto out2;
}
AutoShapeRooter shapeRoot(cx, emptyCallShape);
AutoObjectRooter tvr(cx, FUN_OBJECT(fun));
MUST_FLOW_THROUGH("out");
Bindings bindings(cx);
Bindings bindings(cx, emptyCallShape);
AutoBindingsRooter root(cx, bindings);
for (i = 0; i < nargs; i++) {
argAtom = js_Atomize(cx, argnames[i], strlen(argnames[i]), 0);
@ -4989,6 +4930,12 @@ JS_DecompileScript(JSContext *cx, JSScript *script, const char *name, uintN inde
return str;
}
JS_PUBLIC_API(JSString *)
JS_DecompileScriptObject(JSContext *cx, JSObject *scriptObj, const char *name, uintN indent)
{
return JS_DecompileScript(cx, scriptObj->getScript(), name, indent);
}
JS_PUBLIC_API(JSString *)
JS_DecompileFunction(JSContext *cx, JSFunction *fun, uintN indent)
{
@ -5014,26 +4961,24 @@ JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent)
}
JS_PUBLIC_API(JSBool)
JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval)
JS_ExecuteScript(JSContext *cx, JSObject *obj, JSObject *scriptObj, jsval *rval)
{
JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
JSBool ok;
CHECK_REQUEST(cx);
assertSameCompartment(cx, obj, script);
/* This should receive only scripts handed out via the JSAPI. */
JS_ASSERT(script->u.object);
ok = Execute(cx, obj, script, NULL, 0, Valueify(rval));
assertSameCompartment(cx, obj, scriptObj);
JSBool ok = Execute(cx, obj, scriptObj->getScript(), NULL, 0, Valueify(rval));
LAST_FRAME_CHECKS(cx, ok);
return ok;
}
JS_PUBLIC_API(JSBool)
JS_ExecuteScriptVersion(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval,
JS_ExecuteScriptVersion(JSContext *cx, JSObject *obj, JSObject *scriptObj, jsval *rval,
JSVersion version)
{
AutoVersionAPI ava(cx, version);
return JS_ExecuteScript(cx, obj, script, rval);
return JS_ExecuteScript(cx, obj, scriptObj, rval);
}
bool
@ -6189,20 +6134,6 @@ JS_ClearContextThread(JSContext *cx)
#endif
}
#ifdef MOZ_TRACE_JSCALLS
JS_PUBLIC_API(void)
JS_SetFunctionCallback(JSContext *cx, JSFunctionCallback fcb)
{
cx->functionCallback = fcb;
}
JS_PUBLIC_API(JSFunctionCallback)
JS_GetFunctionCallback(JSContext *cx)
{
return cx->functionCallback;
}
#endif
#ifdef JS_GC_ZEAL
JS_PUBLIC_API(void)
JS_SetGCZeal(JSContext *cx, uint8 zeal)

Просмотреть файл

@ -707,6 +707,14 @@ JS_StrictlyEqual(JSContext *cx, jsval v1, jsval v2, JSBool *equal);
extern JS_PUBLIC_API(JSBool)
JS_SameValue(JSContext *cx, jsval v1, jsval v2, JSBool *same);
/* True iff fun is the global eval function. */
extern JS_PUBLIC_API(JSBool)
JS_IsBuiltinEvalFunction(JSFunction *fun);
/* True iff fun is the Function constructor. */
extern JS_PUBLIC_API(JSBool)
JS_IsBuiltinFunctionConstructor(JSFunction *fun);
/************************************************************************/
/*
@ -988,9 +996,6 @@ JS_SetWrapObjectCallbacks(JSRuntime *rt,
extern JS_PUBLIC_API(JSCrossCompartmentCall *)
JS_EnterCrossCompartmentCall(JSContext *cx, JSObject *target);
extern JS_PUBLIC_API(JSCrossCompartmentCall *)
JS_EnterCrossCompartmentCallScript(JSContext *cx, JSScript *target);
extern JS_PUBLIC_API(void)
JS_LeaveCrossCompartmentCall(JSCrossCompartmentCall *call);
@ -1035,8 +1040,6 @@ class JS_PUBLIC_API(JSAutoEnterCompartment)
bool enter(JSContext *cx, JSObject *target);
bool enter(JSContext *cx, JSScript *target);
void enterAndIgnoreErrors(JSContext *cx, JSObject *target);
bool entered() const { return call != NULL; }
@ -1472,6 +1475,13 @@ inline Anchor<jsval>::~Anchor() {
JS_BEGIN_EXTERN_C
#endif
/*
* C-compatible version of the Anchor class. It should be called after the last
* use of the variable it protects.
*/
extern JS_NEVER_INLINE JS_PUBLIC_API(void)
JS_AnchorPtr(void *p);
/*
* This symbol may be used by embedders to detect the change from the old
* JS_AddRoot(JSContext *, void *) APIs to the new ones above.
@ -1551,13 +1561,6 @@ JS_UnlockGCThingRT(JSRuntime *rt, void *thing);
extern JS_PUBLIC_API(void)
JS_SetExtraGCRoots(JSRuntime *rt, JSTraceDataOp traceOp, void *data);
/*
* For implementors of JSMarkOp. All new code should implement JSTraceOp
* instead.
*/
extern JS_PUBLIC_API(void)
JS_MarkGCThing(JSContext *cx, jsval v, const char *name, void *arg);
/*
* JS_CallTracer API and related macros for implementors of JSTraceOp, to
* enumerate all references to traceable things reachable via a property or
@ -1866,13 +1869,6 @@ JS_RemoveExternalStringFinalizer(JSStringFinalizeOp finalizer);
extern JS_PUBLIC_API(JSString *)
JS_NewExternalString(JSContext *cx, jschar *chars, size_t length, intN type);
/*
* Returns the external-string finalizer index for this string, or -1 if it is
* an "internal" (native to JS engine) string.
*/
extern JS_PUBLIC_API(intN)
JS_GetExternalStringGCType(JSRuntime *rt, JSString *str);
/*
* Deprecated. Use JS_SetNativeStackQuoata instead.
*/
@ -1931,7 +1927,7 @@ struct JSClass {
JSNative construct;
JSXDRObjectOp xdrObject;
JSHasInstanceOp hasInstance;
JSMarkOp mark;
JSTraceOp trace;
JSClassInternal reserved1;
void *reserved[19];
@ -1969,23 +1965,22 @@ struct JSClass {
#define JSCLASS_INTERNAL_FLAG1 (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))
/* Indicates that JSClass.mark is a tracer with JSTraceOp type. */
#define JSCLASS_MARK_IS_TRACE (1<<(JSCLASS_HIGH_FLAGS_SHIFT+3))
#define JSCLASS_INTERNAL_FLAG2 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+4))
#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+5))
#define JSCLASS_FREEZE_CTOR (1<<(JSCLASS_HIGH_FLAGS_SHIFT+6))
/* Additional global reserved slots, beyond those for standard prototypes. */
#define JSRESERVED_GLOBAL_SLOTS_COUNT 6
#define JSRESERVED_GLOBAL_SLOTS_COUNT 7
#define JSRESERVED_GLOBAL_THIS (JSProto_LIMIT * 3)
#define JSRESERVED_GLOBAL_THROWTYPEERROR (JSRESERVED_GLOBAL_THIS + 1)
#define JSRESERVED_GLOBAL_REGEXP_STATICS (JSRESERVED_GLOBAL_THROWTYPEERROR + 1)
#define JSRESERVED_GLOBAL_FUNCTION_NS (JSRESERVED_GLOBAL_REGEXP_STATICS + 1)
#define JSRESERVED_GLOBAL_EVAL_ALLOWED (JSRESERVED_GLOBAL_FUNCTION_NS + 1)
#define JSRESERVED_GLOBAL_FLAGS (JSRESERVED_GLOBAL_EVAL_ALLOWED + 1)
#define JSRESERVED_GLOBAL_EVAL (JSRESERVED_GLOBAL_EVAL_ALLOWED + 1)
#define JSRESERVED_GLOBAL_FLAGS (JSRESERVED_GLOBAL_EVAL + 1)
/* Global flags. */
#define JSGLOBAL_FLAGS_CLEARED 0x1
@ -2016,7 +2011,7 @@ struct JSClass {
& JSCLASS_CACHED_PROTO_MASK))
/* Initializer for unused members of statically initialized JSClass structs. */
#define JSCLASS_NO_INTERNAL_MEMBERS 0,{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
#define JSCLASS_NO_INTERNAL_MEMBERS 0,{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
#define JSCLASS_NO_OPTIONAL_MEMBERS 0,0,0,0,0,0,0,JSCLASS_NO_INTERNAL_MEMBERS
struct JSIdArray {
@ -2658,96 +2653,60 @@ extern JS_PUBLIC_API(JSBool)
JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj,
const char *bytes, size_t length);
/*
* The JSScript objects returned by the following functions refer to string and
* other kinds of literals, including doubles and RegExp objects. These
* literals are vulnerable to garbage collection; to root script objects and
* prevent literals from being collected, create a rootable object using
* JS_NewScriptObject, and root the resulting object using JS_Add[Named]Root.
*/
extern JS_PUBLIC_API(JSScript *)
extern JS_PUBLIC_API(JSObject *)
JS_CompileScript(JSContext *cx, JSObject *obj,
const char *bytes, size_t length,
const char *filename, uintN lineno);
extern JS_PUBLIC_API(JSScript *)
extern JS_PUBLIC_API(JSObject *)
JS_CompileScriptForPrincipals(JSContext *cx, JSObject *obj,
JSPrincipals *principals,
const char *bytes, size_t length,
const char *filename, uintN lineno);
extern JS_PUBLIC_API(JSScript *)
extern JS_PUBLIC_API(JSObject *)
JS_CompileScriptForPrincipalsVersion(JSContext *cx, JSObject *obj,
JSPrincipals *principals,
const char *bytes, size_t length,
const char *filename, uintN lineno,
JSVersion version);
extern JS_PUBLIC_API(JSScript *)
extern JS_PUBLIC_API(JSObject *)
JS_CompileUCScript(JSContext *cx, JSObject *obj,
const jschar *chars, size_t length,
const char *filename, uintN lineno);
extern JS_PUBLIC_API(JSScript *)
extern JS_PUBLIC_API(JSObject *)
JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj,
JSPrincipals *principals,
const jschar *chars, size_t length,
const char *filename, uintN lineno);
extern JS_PUBLIC_API(JSScript *)
extern JS_PUBLIC_API(JSObject *)
JS_CompileUCScriptForPrincipalsVersion(JSContext *cx, JSObject *obj,
JSPrincipals *principals,
const jschar *chars, size_t length,
const char *filename, uintN lineno,
JSVersion version);
extern JS_PUBLIC_API(JSScript *)
extern JS_PUBLIC_API(JSObject *)
JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename);
extern JS_PUBLIC_API(JSScript *)
extern JS_PUBLIC_API(JSObject *)
JS_CompileFileHandle(JSContext *cx, JSObject *obj, const char *filename,
FILE *fh);
extern JS_PUBLIC_API(JSScript *)
extern JS_PUBLIC_API(JSObject *)
JS_CompileFileHandleForPrincipals(JSContext *cx, JSObject *obj,
const char *filename, FILE *fh,
JSPrincipals *principals);
extern JS_PUBLIC_API(JSScript *)
extern JS_PUBLIC_API(JSObject *)
JS_CompileFileHandleForPrincipalsVersion(JSContext *cx, JSObject *obj,
const char *filename, FILE *fh,
JSPrincipals *principals,
JSVersion version);
/*
* NB: you must use JS_NewScriptObject and root a pointer to its return value
* in order to keep a JSScript and its atoms safe from garbage collection after
* creating the script via JS_Compile* and before a JS_ExecuteScript* call.
* E.g., and without error checks:
*
* JSScript *script = JS_CompileFile(cx, global, filename);
* JSObject *scrobj = JS_NewScriptObject(cx, script);
* JS_AddNamedObjectRoot(cx, &scrobj, "scrobj");
* do {
* jsval result;
* JS_ExecuteScript(cx, global, script, &result);
* JS_GC();
* } while (!JSVAL_IS_BOOLEAN(result) || JSVAL_TO_BOOLEAN(result));
* JS_RemoveObjectRoot(cx, &scrobj);
*/
extern JS_PUBLIC_API(JSObject *)
JS_NewScriptObject(JSContext *cx, JSScript *script);
/*
* Infallible getter for a script's object. If JS_NewScriptObject has not been
* called on script yet, the return value will be null.
*/
extern JS_PUBLIC_API(JSObject *)
JS_GetScriptObject(JSScript *script);
extern JS_PUBLIC_API(void)
JS_DestroyScript(JSContext *cx, JSScript *script);
extern JS_PUBLIC_API(JSFunction *)
JS_CompileFunction(JSContext *cx, JSObject *obj, const char *name,
uintN nargs, const char **argnames,
@ -2783,8 +2742,7 @@ JS_CompileUCFunctionForPrincipalsVersion(JSContext *cx, JSObject *obj,
JSVersion version);
extern JS_PUBLIC_API(JSString *)
JS_DecompileScript(JSContext *cx, JSScript *script, const char *name,
uintN indent);
JS_DecompileScriptObject(JSContext *cx, JSObject *scriptObj, const char *name, uintN indent);
/*
* API extension: OR this into indent to avoid pretty-printing the decompiled
@ -2834,10 +2792,10 @@ JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent);
* etc., entry points.
*/
extern JS_PUBLIC_API(JSBool)
JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval);
JS_ExecuteScript(JSContext *cx, JSObject *obj, JSObject *scriptObj, jsval *rval);
extern JS_PUBLIC_API(JSBool)
JS_ExecuteScriptVersion(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval,
JS_ExecuteScriptVersion(JSContext *cx, JSObject *obj, JSObject *scriptObj, jsval *rval,
JSVersion version);
/*
@ -3780,28 +3738,6 @@ JS_SetContextThread(JSContext *cx);
extern JS_PUBLIC_API(jsword)
JS_ClearContextThread(JSContext *cx);
#ifdef MOZ_TRACE_JSCALLS
typedef void (*JSFunctionCallback)(const JSFunction *fun,
const JSScript *scr,
const JSContext *cx,
int entering);
/*
* The callback is expected to be quick and noninvasive. It should not
* trigger interrupts, turn on debugging, or produce uncaught JS
* exceptions. The state of the stack and registers in the context
* cannot be relied upon, since this callback may be invoked directly
* from either JIT. The 'entering' field means we are entering a
* function if it is positive, leaving a function if it is zero or
* negative.
*/
extern JS_PUBLIC_API(void)
JS_SetFunctionCallback(JSContext *cx, JSFunctionCallback fcb);
extern JS_PUBLIC_API(JSFunctionCallback)
JS_GetFunctionCallback(JSContext *cx);
#endif
/************************************************************************/
/*

Просмотреть файл

@ -87,11 +87,9 @@
#include "jsatom.h"
#include "jsbit.h"
#include "jsbool.h"
#include "jstracer.h"
#include "jsbuiltins.h"
#include "jscntxt.h"
#include "jsversion.h"
#include "jsdbgapi.h" /* for js_TraceWatchPoints */
#include "jsfun.h"
#include "jsgc.h"
#include "jsinterp.h"
@ -102,6 +100,7 @@
#include "jsscope.h"
#include "jsstr.h"
#include "jsstaticcheck.h"
#include "jstracer.h"
#include "jsvector.h"
#include "jswrapper.h"
@ -945,8 +944,7 @@ array_trace(JSTracer *trc, JSObject *obj)
JS_ASSERT(obj->isDenseArray());
uint32 capacity = obj->getDenseArrayCapacity();
for (uint32 i = 0; i < capacity; i++)
MarkValue(trc, obj->getDenseArrayElement(i), "dense_array_elems");
MarkValueRange(trc, capacity, obj->slots, "element");
}
static JSBool
@ -985,7 +983,7 @@ Class js_ArrayClass = {
NULL, /* construct */
NULL, /* xdrObject */
NULL, /* hasInstance */
NULL, /* mark */
array_trace, /* trace */
JS_NULL_CLASS_EXT,
{
array_lookupProperty,
@ -997,7 +995,6 @@ Class js_ArrayClass = {
array_deleteProperty,
NULL, /* enumerate */
array_typeOf,
array_trace,
array_fix,
NULL, /* thisObject */
NULL, /* clear */
@ -3259,7 +3256,7 @@ js_CloneDensePrimitiveArray(JSContext *cx, JSObject *obj, JSObject **clone)
*/
jsuint jsvalCount = JS_MIN(obj->getDenseArrayCapacity(), length);
js::AutoValueVector vector(cx);
AutoValueVector vector(cx);
if (!vector.reserve(jsvalCount))
return JS_FALSE;
@ -3279,7 +3276,7 @@ js_CloneDensePrimitiveArray(JSContext *cx, JSObject *obj, JSObject **clone)
return JS_TRUE;
}
vector.append(val);
vector.infallibleAppend(val);
}
*clone = NewDenseCopiedArray(cx, jsvalCount, vector.begin());

Просмотреть файл

@ -55,7 +55,6 @@
#include "jslock.h"
#include "jsnum.h"
#include "jsparse.h"
#include "jsscan.h"
#include "jsstr.h"
#include "jsversion.h"
#include "jsxml.h"
@ -477,10 +476,9 @@ js_AtomizeString(JSContext *cx, JSString *strArg, uintN flags)
if (staticStr)
return STRING_TO_ATOM(staticStr);
JSAtomState *state = &cx->runtime->atomState;
AtomSet &atoms = state->atoms;
AutoLockAtomsCompartment lock(cx);
AtomSet &atoms = cx->runtime->atomState.atoms;
AtomSet::AddPtr p = atoms.lookupForAdd(str);
/* Hashing the string should have flattened it if it was a rope. */
@ -491,49 +489,27 @@ js_AtomizeString(JSContext *cx, JSString *strArg, uintN flags)
key = AtomEntryToKey(*p);
} else {
/*
* Ensure that any atomized string lives only in the default
* compartment.
* We have to relookup the key as the last ditch GC invoked from the
* string allocation or OOM handling may unlock the atomsCompartment.
*/
bool needNewString = !!(flags & ATOM_TMPSTR) ||
str->asCell()->compartment() != cx->runtime->atomsCompartment;
/*
* Unless str is already comes from the default compartment and flat,
* we have to relookup the key as the last ditch GC invoked from the
* string allocation or OOM handling may unlock the default
* compartment lock.
*/
if (!needNewString && str->isFlat()) {
str->flatClearExtensible();
key = str;
atoms.add(p, StringToInitialAtomEntry(key));
} else {
if (needNewString) {
SwitchToCompartment sc(cx, cx->runtime->atomsCompartment);
if (flags & ATOM_NOCOPY) {
key = js_NewString(cx, const_cast<jschar *>(str->flatChars()), length);
if (!key)
return NULL;
/* Finish handing off chars to the GC'ed key string. */
JS_ASSERT(flags & ATOM_TMPSTR);
str->u.chars = NULL;
} else {
key = js_NewStringCopyN(cx, chars, length);
if (!key)
return NULL;
}
} else {
JS_ASSERT(str->isDependent());
if (!str->undepend(cx))
return NULL;
key = str;
}
if (!atoms.relookupOrAdd(p, key, StringToInitialAtomEntry(key))) {
JS_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report */
SwitchToCompartment sc(cx, cx->runtime->atomsCompartment);
if (flags & ATOM_NOCOPY) {
key = js_NewString(cx, const_cast<jschar *>(str->flatChars()), length);
if (!key)
return NULL;
}
/* Finish handing off chars to the GC'ed key string. */
JS_ASSERT(flags & ATOM_TMPSTR);
str->u.chars = NULL;
} else {
key = js_NewStringCopyN(cx, chars, length);
if (!key)
return NULL;
}
if (!atoms.relookupOrAdd(p, key, StringToInitialAtomEntry(key))) {
JS_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report */
return NULL;
}
key->flatSetAtomized();
}
@ -545,7 +521,7 @@ js_AtomizeString(JSContext *cx, JSString *strArg, uintN flags)
}
JSAtom *
js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags)
js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags, bool useCESU8)
{
jschar *chars;
JSString str;
@ -565,12 +541,15 @@ js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags)
size_t inflatedLength = ATOMIZE_BUF_MAX - 1;
if (length < ATOMIZE_BUF_MAX) {
js_InflateStringToBuffer(cx, bytes, length, inflated, &inflatedLength);
if (useCESU8)
js_InflateUTF8StringToBuffer(cx, bytes, length, inflated, &inflatedLength, true);
else
js_InflateStringToBuffer(cx, bytes, length, inflated, &inflatedLength);
inflated[inflatedLength] = 0;
chars = inflated;
} else {
inflatedLength = length;
chars = js_InflateString(cx, bytes, &inflatedLength);
chars = js_InflateString(cx, bytes, &inflatedLength, useCESU8);
if (!chars)
return NULL;
flags |= ATOM_NOCOPY;

Просмотреть файл

@ -572,7 +572,7 @@ extern JSAtom *
js_AtomizeString(JSContext *cx, JSString *str, uintN flags);
extern JSAtom *
js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags);
js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags, bool useCESU8 = false);
extern JSAtom *
js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags);

Просмотреть файл

@ -73,7 +73,6 @@
#include "jsobj.h"
#include "jsopcode.h"
#include "jspubtd.h"
#include "jsscan.h"
#include "jsscope.h"
#include "jsscript.h"
#include "jsstaticcheck.h"
@ -139,6 +138,19 @@ StackSegment::contains(const JSStackFrame *fp) const
}
#endif
JSStackFrame *
StackSegment::computeNextFrame(JSStackFrame *fp) const
{
JS_ASSERT(contains(fp));
JS_ASSERT(fp != getCurrentFrame());
JSStackFrame *next = getCurrentFrame();
JSStackFrame *prev;
while ((prev = next->prev()) != fp)
next = prev;
return next;
}
bool
StackSpace::init()
{
@ -1119,11 +1131,7 @@ FreeContext(JSContext *cx)
cx->free(temp);
}
/* Destroy the resolve recursion damper. */
if (cx->resolvingTable) {
JS_DHashTableDestroy(cx->resolvingTable);
cx->resolvingTable = NULL;
}
JS_ASSERT(!cx->resolvingList);
/* Finally, free cx itself. */
cx->~JSContext();
@ -1158,107 +1166,22 @@ js_NextActiveContext(JSRuntime *rt, JSContext *cx)
#endif
}
static JSDHashNumber
resolving_HashKey(JSDHashTable *table, const void *ptr)
{
const JSResolvingKey *key = (const JSResolvingKey *)ptr;
namespace js {
return (JSDHashNumber(uintptr_t(key->obj)) >> JS_GCTHING_ALIGN) ^ JSID_BITS(key->id);
bool
AutoResolving::alreadyStartedSlow() const
{
JS_ASSERT(link);
AutoResolving *cursor = link;
do {
JS_ASSERT(this != cursor);
if (object == cursor->object && id == cursor->id && kind == cursor->kind)
return true;
} while (!!(cursor = cursor->link));
return false;
}
static JSBool
resolving_MatchEntry(JSDHashTable *table,
const JSDHashEntryHdr *hdr,
const void *ptr)
{
const JSResolvingEntry *entry = (const JSResolvingEntry *)hdr;
const JSResolvingKey *key = (const JSResolvingKey *)ptr;
return entry->key.obj == key->obj && entry->key.id == key->id;
}
static const JSDHashTableOps resolving_dhash_ops = {
JS_DHashAllocTable,
JS_DHashFreeTable,
resolving_HashKey,
resolving_MatchEntry,
JS_DHashMoveEntryStub,
JS_DHashClearEntryStub,
JS_DHashFinalizeStub,
NULL
};
JSBool
js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
JSResolvingEntry **entryp)
{
JSDHashTable *table;
JSResolvingEntry *entry;
table = cx->resolvingTable;
if (!table) {
table = JS_NewDHashTable(&resolving_dhash_ops, NULL,
sizeof(JSResolvingEntry),
JS_DHASH_MIN_SIZE);
if (!table)
goto outofmem;
cx->resolvingTable = table;
}
entry = (JSResolvingEntry *)
JS_DHashTableOperate(table, key, JS_DHASH_ADD);
if (!entry)
goto outofmem;
if (entry->flags & flag) {
/* An entry for (key, flag) exists already -- dampen recursion. */
entry = NULL;
} else {
/* Fill in key if we were the first to add entry, then set flag. */
if (!entry->key.obj)
entry->key = *key;
entry->flags |= flag;
}
*entryp = entry;
return JS_TRUE;
outofmem:
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
void
js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
JSResolvingEntry *entry, uint32 generation)
{
JSDHashTable *table;
/*
* Clear flag from entry->flags and return early if other flags remain.
* We must take care to re-lookup entry if the table has changed since
* it was found by js_StartResolving.
*/
table = cx->resolvingTable;
if (!entry || table->generation != generation) {
entry = (JSResolvingEntry *)
JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP);
}
JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&entry->hdr));
entry->flags &= ~flag;
if (entry->flags)
return;
/*
* Do a raw remove only if fewer entries were removed than would cause
* alpha to be less than .5 (alpha is at most .75). Otherwise, we just
* call JS_DHashTableOperate to re-lookup the key and remove its entry,
* compressing or shrinking the table as needed.
*/
if (table->removedCount < JS_DHASH_TABLE_SIZE(table) >> 2)
JS_DHashTableRawRemove(table, &entry->hdr);
else
JS_DHashTableOperate(table, key, JS_DHASH_REMOVE);
}
} /* namespace js */
static void
ReportError(JSContext *cx, const char *message, JSErrorReport *reportp,

Просмотреть файл

@ -419,6 +419,8 @@ class StackSegment
#ifdef DEBUG
JS_REQUIRES_STACK bool contains(const JSStackFrame *fp) const;
#endif
JSStackFrame *computeNextFrame(JSStackFrame *fp) const;
};
static const size_t VALUES_PER_STACK_SEGMENT = sizeof(StackSegment) / sizeof(Value);
@ -986,7 +988,7 @@ typedef void
namespace js {
typedef js::Vector<JSCompartment *, 0, js::SystemAllocPolicy> WrapperVector;
typedef js::Vector<JSCompartment *, 0, js::SystemAllocPolicy> CompartmentVector;
}
@ -998,7 +1000,7 @@ struct JSRuntime {
#endif
/* List of compartments (protected by the GC lock). */
js::WrapperVector compartments;
js::CompartmentVector compartments;
/* Runtime state, synchronized by the stateChange/gcLock condvar/lock. */
JSRuntimeState state;
@ -1464,32 +1466,12 @@ struct JSArgumentFormatMap {
};
#endif
/*
* Key and entry types for the JSContext.resolvingTable hash table, typedef'd
* here because all consumers need to see these declarations (and not just the
* typedef names, as would be the case for an opaque pointer-to-typedef'd-type
* declaration), along with cx->resolvingTable.
*/
typedef struct JSResolvingKey {
JSObject *obj;
jsid id;
} JSResolvingKey;
typedef struct JSResolvingEntry {
JSDHashEntryHdr hdr;
JSResolvingKey key;
uint32 flags;
} JSResolvingEntry;
#define JSRESFLAG_LOOKUP 0x1 /* resolving id from lookup */
#define JSRESFLAG_WATCH 0x2 /* resolving id from watch */
#define JSRESOLVE_INFER 0xffff /* infer bits from current bytecode */
extern const JSDebugHooks js_NullDebugHooks; /* defined in jsdbgapi.cpp */
namespace js {
class AutoGCRooter;
struct AutoResolving;
static inline bool
OptionsHasXML(uint32 options)
@ -1639,13 +1621,7 @@ struct JSContext
/* Locale specific callbacks for string conversion. */
JSLocaleCallbacks *localeCallbacks;
/*
* cx->resolvingTable is non-null and non-empty if we are initializing
* standard classes lazily, or if we are otherwise recursing indirectly
* from js_LookupProperty through a Class.resolve hook. It is used to
* limit runaway recursion (see jsapi.c and jsobj.c).
*/
JSDHashTable *resolvingTable;
js::AutoResolving *resolvingList;
/*
* True if generating an error, to prevent runaway recursion.
@ -1784,9 +1760,6 @@ struct JSContext
/* Undoes calls to suspendActiveSegment. */
void restoreSegment();
/* Get the frame whose prev() is fp, which may be in any segment. */
inline JSStackFrame *computeNextFrame(JSStackFrame *fp);
/*
* Perform a linear search of all frames in all segments in the given context
* for the given frame, returning the segment, if found, and null otherwise.
@ -2182,6 +2155,42 @@ FrameAtomBase(JSContext *cx, JSStackFrame *fp)
namespace js {
struct AutoResolving {
public:
enum Kind {
LOOKUP,
WATCH
};
AutoResolving(JSContext *cx, JSObject *obj, jsid id, Kind kind = LOOKUP
JS_GUARD_OBJECT_NOTIFIER_PARAM)
: context(cx), object(obj), id(id), kind(kind), link(cx->resolvingList)
{
JS_GUARD_OBJECT_NOTIFIER_INIT;
JS_ASSERT(obj);
cx->resolvingList = this;
}
~AutoResolving() {
JS_ASSERT(context->resolvingList == this);
context->resolvingList = link;
}
bool alreadyStarted() const {
return link && alreadyStartedSlow();
}
private:
bool alreadyStartedSlow() const;
JSContext *const context;
JSObject *const object;
jsid const id;
Kind const kind;
AutoResolving *const link;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
};
class AutoGCRooter {
public:
AutoGCRooter(JSContext *cx, ptrdiff_t tag)
@ -2978,17 +2987,6 @@ js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp);
extern JS_FRIEND_API(JSContext *)
js_NextActiveContext(JSRuntime *, JSContext *);
/*
* Class.resolve and watchpoint recursion damping machinery.
*/
extern JSBool
js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
JSResolvingEntry **entryp);
extern void
js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
JSResolvingEntry *entry, uint32 generation);
/*
* Report an exception, which is currently realized as a printf-style format
* string and its arguments.
@ -3242,6 +3240,9 @@ class AutoVectorRooter : protected AutoGCRooter
bool append(const T &v) { return vector.append(v); }
/* For use when space has already been reserved. */
void infallibleAppend(const T &v) { vector.infallibleAppend(v); }
void popBack() { vector.popBack(); }
bool growBy(size_t inc) {

Просмотреть файл

@ -94,21 +94,6 @@ JSContext::ensureGeneratorStackSpace()
return ok;
}
JSStackFrame *
JSContext::computeNextFrame(JSStackFrame *fp)
{
JSStackFrame *next = NULL;
for (js::StackSegment *ss = currentSegment; ; ss = ss->getPreviousInContext()) {
JSStackFrame *end = ss->getInitialFrame()->prev();
for (JSStackFrame *f = ss->getCurrentFrame(); f != end; next = f, f = f->prev()) {
if (f == fp)
return next;
}
if (end != ss->getPreviousInContext()->getCurrentFrame())
next = NULL;
}
}
inline js::RegExpStatics *
JSContext::regExpStatics()
{

Просмотреть файл

@ -52,6 +52,7 @@
#include "methodjit/MonoIC.h"
#include "jsgcinlines.h"
#include "jsscopeinlines.h"
#if ENABLE_YARR_JIT
#include "assembler/jit/ExecutableAllocator.h"
@ -66,18 +67,24 @@ JSCompartment::JSCompartment(JSRuntime *rt)
gcBytes(0),
gcTriggerBytes(0),
gcLastBytes(0),
hold(false),
data(NULL),
active(false),
#ifdef JS_METHODJIT
jaegerCompartment(NULL),
#endif
propertyTree(thisForCtor()),
emptyArgumentsShape(NULL),
emptyBlockShape(NULL),
emptyCallShape(NULL),
emptyDeclEnvShape(NULL),
emptyEnumeratorShape(NULL),
emptyWithShape(NULL),
debugMode(rt->debugMode),
#if ENABLE_YARR_JIT
regExpAllocator(NULL),
#endif
mathCache(NULL),
marked(false)
mathCache(NULL)
{
JS_INIT_CLIST(&scripts);
@ -91,7 +98,6 @@ JSCompartment::JSCompartment(JSRuntime *rt)
JSCompartment::~JSCompartment()
{
Shape::finishEmptyShapes(this);
propertyTree.finish();
#if ENABLE_YARR_JIT
@ -138,19 +144,13 @@ JSCompartment::init()
}
#endif
if (!Shape::initEmptyShapes(this))
return false;
#ifdef JS_TRACER
if (!InitJIT(&traceMonitor))
return false;
#endif
if (!toSourceCache.init())
return false;
#if ENABLE_YARR_JIT
regExpAllocator = JSC::ExecutableAllocator::create();
regExpAllocator = js_new<JSC::ExecutableAllocator>();
if (!regExpAllocator)
return false;
#endif
@ -448,48 +448,23 @@ ScriptPoolDestroyed(JSContext *cx, mjit::JITScript *jit,
/*
* This method marks pointers that cross compartment boundaries. It should be
* called only by per-compartment GCs, since full GCs naturally follow pointers
* called only for per-compartment GCs, since full GCs naturally follow pointers
* across compartments.
*/
void
JSCompartment::markCrossCompartment(JSTracer *trc)
JSCompartment::markCrossCompartmentWrappers(JSTracer *trc)
{
JS_ASSERT(trc->context->runtime->gcCurrentCompartment);
for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront())
MarkValue(trc, e.front().key, "cross-compartment wrapper");
}
void
JSCompartment::mark(JSTracer *trc)
{
if (IS_GC_MARKING_TRACER(trc)) {
JSRuntime *rt = trc->context->runtime;
if (rt->gcCurrentCompartment && rt->gcCurrentCompartment != this)
return;
if (marked)
return;
marked = true;
}
if (emptyArgumentsShape)
emptyArgumentsShape->trace(trc);
if (emptyBlockShape)
emptyBlockShape->trace(trc);
if (emptyCallShape)
emptyCallShape->trace(trc);
if (emptyDeclEnvShape)
emptyDeclEnvShape->trace(trc);
if (emptyEnumeratorShape)
emptyEnumeratorShape->trace(trc);
if (emptyWithShape)
emptyWithShape->trace(trc);
}
void
JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
{
chunk = NULL;
/* Remove dead wrappers from the table. */
for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
JS_ASSERT_IF(IsAboutToBeFinalized(cx, e.front().key.toGCThing()) &&
@ -501,6 +476,20 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
}
}
/* Remove dead empty shapes. */
if (emptyArgumentsShape && !emptyArgumentsShape->marked())
emptyArgumentsShape = NULL;
if (emptyBlockShape && !emptyBlockShape->marked())
emptyBlockShape = NULL;
if (emptyCallShape && !emptyCallShape->marked())
emptyCallShape = NULL;
if (emptyDeclEnvShape && !emptyDeclEnvShape->marked())
emptyDeclEnvShape = NULL;
if (emptyEnumeratorShape && !emptyEnumeratorShape->marked())
emptyEnumeratorShape = NULL;
if (emptyWithShape && !emptyWithShape->marked())
emptyWithShape = NULL;
#ifdef JS_TRACER
traceMonitor.sweep(cx);
#endif
@ -550,7 +539,7 @@ JSCompartment::purge(JSContext *cx)
js_DestroyScriptsToGC(cx, this);
nativeIterCache.purge();
toSourceCache.clear();
toSourceCache.destroyIfConstructed();
#ifdef JS_TRACER
/*

Просмотреть файл

@ -381,6 +381,8 @@ struct JS_FRIEND_API(JSCompartment) {
size_t gcTriggerBytes;
size_t gcLastBytes;
bool hold;
#ifdef JS_GCMETER
js::gc::JSGCArenaStats compartmentStats[js::gc::FINALIZE_LIMIT];
#endif
@ -442,7 +444,8 @@ struct JS_FRIEND_API(JSCompartment) {
js::NativeIterCache nativeIterCache;
js::ToSourceCache toSourceCache;
typedef js::LazilyConstructed<js::ToSourceCache> LazyToSourceCache;
LazyToSourceCache toSourceCache;
JSCompartment(JSRuntime *rt);
~JSCompartment();
@ -450,10 +453,7 @@ struct JS_FRIEND_API(JSCompartment) {
bool init();
/* Mark cross-compartment wrappers. */
void markCrossCompartment(JSTracer *trc);
/* Mark this compartment's local roots. */
void mark(JSTracer *trc);
void markCrossCompartmentWrappers(JSTracer *trc);
bool wrap(JSContext *cx, js::Value *vp);
bool wrap(JSContext *cx, JSString **strp);
@ -480,8 +480,6 @@ struct JS_FRIEND_API(JSCompartment) {
js::MathCache *allocMathCache(JSContext *cx);
bool marked;
typedef js::HashMap<jsbytecode*,
size_t,
js::DefaultHasher<jsbytecode*>,
@ -495,9 +493,6 @@ struct JS_FRIEND_API(JSCompartment) {
return mathCache ? mathCache : allocMathCache(cx);
}
bool isMarked() { return marked; }
void clearMark() { marked = false; }
size_t backEdgeCount(jsbytecode *pc) const;
size_t incBackEdgeCount(jsbytecode *pc);
};
@ -605,13 +600,22 @@ class PreserveCompartment {
class SwitchToCompartment : public PreserveCompartment {
public:
SwitchToCompartment(JSContext *cx, JSCompartment *newCompartment) : PreserveCompartment(cx) {
SwitchToCompartment(JSContext *cx, JSCompartment *newCompartment
JS_GUARD_OBJECT_NOTIFIER_PARAM)
: PreserveCompartment(cx)
{
JS_GUARD_OBJECT_NOTIFIER_INIT;
cx->compartment = newCompartment;
}
SwitchToCompartment(JSContext *cx, JSObject *target) : PreserveCompartment(cx) {
SwitchToCompartment(JSContext *cx, JSObject *target JS_GUARD_OBJECT_NOTIFIER_PARAM)
: PreserveCompartment(cx)
{
JS_GUARD_OBJECT_NOTIFIER_INIT;
cx->compartment = target->getCompartment();
}
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
};
class AssertCompartmentUnchanged {

Просмотреть файл

@ -116,10 +116,8 @@ JS_SetRuntimeDebugMode(JSRuntime *rt, JSBool debug)
static bool
CompartmentHasLiveScripts(JSCompartment *comp)
{
#ifdef JS_METHODJIT
# ifdef JS_THREADSAFE
#if defined(JS_METHODJIT) && defined(JS_THREADSAFE)
jsword currentThreadId = reinterpret_cast<jsword>(js_CurrentThreadId());
# endif
#endif
// Unsynchronized context iteration is technically a race; but this is only
@ -127,7 +125,7 @@ CompartmentHasLiveScripts(JSCompartment *comp)
JSContext *iter = NULL;
JSContext *icx;
while ((icx = JS_ContextIterator(comp->rt, &iter))) {
#ifdef JS_THREADSAFE
#if defined(JS_METHODJIT) && defined(JS_THREADSAFE)
if (JS_GetContextThread(icx) != currentThreadId)
continue;
#endif
@ -160,7 +158,7 @@ JS_SetDebugModeForCompartment(JSContext *cx, JSCompartment *comp, JSBool debug)
// assumes that 'comp' is in the same thread as 'cx'.
#ifdef JS_METHODJIT
JSAutoEnterCompartment ac;
JS::AutoEnterScriptCompartment ac;
for (JSScript *script = (JSScript *)comp->scripts.next;
&script->links != &comp->scripts;
@ -191,6 +189,8 @@ JS_SetDebugModeForCompartment(JSContext *cx, JSCompartment *comp, JSBool debug)
JS_FRIEND_API(JSBool)
js_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep)
{
assertSameCompartment(cx, script);
#ifdef JS_METHODJIT
if (!script->singleStepMode == !singleStep)
return JS_TRUE;
@ -233,6 +233,7 @@ CheckDebugMode(JSContext *cx)
JS_PUBLIC_API(JSBool)
JS_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep)
{
assertSameCompartment(cx, script);
if (!CheckDebugMode(cx))
return JS_FALSE;
@ -474,6 +475,7 @@ JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval)
jsint op;
JSTrapStatus status;
assertSameCompartment(cx, script);
DBG_LOCK(cx->runtime);
trap = FindTrap(cx->runtime, script, pc);
JS_ASSERT(!trap || trap->handler);
@ -627,26 +629,43 @@ DropWatchPointAndUnlock(JSContext *cx, JSWatchPoint *wp, uintN flag)
/*
* NB: js_TraceWatchPoints does not acquire cx->runtime->debuggerLock, since
* the debugger should never be racing with the GC (i.e., the debugger must
* respect the request model).
* respect the request model). If any unmarked objects were marked, this
* function returns true and the GC will iteratively call this function again
* until no more unmarked heap objects are found. This is necessary because
* watch points have a weak pointer semantics.
*/
void
js_TraceWatchPoints(JSTracer *trc, JSObject *obj)
JSBool
js_TraceWatchPoints(JSTracer *trc)
{
JSRuntime *rt;
JSWatchPoint *wp;
rt = trc->context->runtime;
bool modified = false;
for (wp = (JSWatchPoint *)rt->watchPointList.next;
&wp->links != &rt->watchPointList;
wp = (JSWatchPoint *)wp->links.next) {
if (wp->object == obj) {
wp->shape->trace(trc);
if (wp->shape->hasSetterValue() && wp->setter)
MarkObject(trc, *CastAsObject(wp->setter), "wp->setter");
MarkObject(trc, *wp->closure, "wp->closure");
if (wp->object->isMarked()) {
if (!wp->shape->marked()) {
modified = true;
wp->shape->trace(trc);
}
if (wp->shape->hasSetterValue() && wp->setter) {
if (!CastAsObject(wp->setter)->isMarked()) {
modified = true;
MarkObject(trc, *CastAsObject(wp->setter), "wp->setter");
}
}
if (!wp->closure->isMarked()) {
modified = true;
MarkObject(trc, *wp->closure, "wp->closure");
}
}
}
return modified;
}
void
@ -708,6 +727,7 @@ FindWatchPoint(JSRuntime *rt, JSObject *obj, jsid id)
JSBool
js_watch_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
{
assertSameCompartment(cx, obj);
JSRuntime *rt = cx->runtime;
DBG_LOCK(rt);
for (JSWatchPoint *wp = (JSWatchPoint *)rt->watchPointList.next;
@ -715,19 +735,30 @@ js_watch_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
wp = (JSWatchPoint *)wp->links.next) {
const Shape *shape = wp->shape;
if (wp->object == obj && SHAPE_USERID(shape) == id && !(wp->flags & JSWP_HELD)) {
bool ok;
Value old;
uint32 slot;
const Shape *needMethodSlotWrite = NULL;
wp->flags |= JSWP_HELD;
DBG_UNLOCK(rt);
jsid propid = shape->id;
shape = obj->nativeLookup(propid);
if (!shape) {
/*
* This happens if the watched property has been deleted, but a
* prototype has a watched accessor property with the same
* name. See bug 636697.
*/
ok = true;
goto out;
}
JS_ASSERT(IsWatchedProperty(cx, shape));
jsid userid = SHAPE_USERID(shape);
/* Determine the property's old value. */
bool ok;
uint32 slot = shape->slot;
Value old = obj->containsSlot(slot) ? obj->nativeGetSlot(slot) : UndefinedValue();
const Shape *needMethodSlotWrite = NULL;
slot = shape->slot;
old = obj->containsSlot(slot) ? obj->nativeGetSlot(slot) : UndefinedValue();
if (shape->isMethod()) {
/*
* We get here in two cases: (1) the existing watched property
@ -790,7 +821,8 @@ js_watch_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
? ExternalInvoke(cx, ObjectValue(*obj),
ObjectValue(*CastAsObject(wp->setter)),
1, vp, vp)
: CallJSPropertyOpSetter(cx, wp->setter, obj, userid, strict, vp);
: CallJSPropertyOpSetter(cx, wp->setter, obj, SHAPE_USERID(shape),
strict, vp);
} else if (shape == needMethodSlotWrite) {
/* See comment above about needMethodSlotWrite. */
obj->nativeSetSlot(shape->slot, *vp);
@ -932,6 +964,8 @@ UpdateWatchpointShape(JSContext *cx, JSWatchPoint *wp, const Shape *newShape)
const Shape *
js_SlowPathUpdateWatchpointsForShape(JSContext *cx, JSObject *obj, const Shape *newShape)
{
assertSameCompartment(cx, obj);
/*
* The watchpoint code uses the normal property-modification functions to install its
* own watchpoint-aware shapes. Those functions report those changes back to the
@ -978,6 +1012,8 @@ JS_PUBLIC_API(JSBool)
JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsid id,
JSWatchPointHandler handler, JSObject *closure)
{
assertSameCompartment(cx, obj);
JSObject *origobj;
Value v;
uintN attrs;
@ -986,14 +1022,14 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsid id,
origobj = obj;
OBJ_TO_INNER_OBJECT(cx, obj);
if (!obj)
return JS_FALSE;
return false;
AutoValueRooter idroot(cx);
if (JSID_IS_INT(id)) {
propid = id;
} else {
if (!js_ValueToStringId(cx, IdToValue(id), &propid))
return JS_FALSE;
return false;
propid = js_CheckForStringIndex(propid);
idroot.set(IdToValue(propid));
}
@ -1003,18 +1039,18 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsid id,
* again to make sure that we're allowed to set a watch point.
*/
if (origobj != obj && !CheckAccess(cx, obj, propid, JSACC_WATCH, &v, &attrs))
return JS_FALSE;
return false;
if (!obj->isNative()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH,
obj->getClass()->name);
return JS_FALSE;
return false;
}
JSObject *pobj;
JSProperty *prop;
if (!js_LookupProperty(cx, obj, propid, &pobj, &prop))
return JS_FALSE;
return false;
const Shape *shape = (Shape *) prop;
JSRuntime *rt = cx->runtime;
if (!shape) {
@ -1024,7 +1060,7 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsid id,
/* Make a new property in obj so we can watch for the first set. */
if (!js_DefineNativeProperty(cx, obj, propid, UndefinedValue(), NULL, NULL,
JSPROP_ENUMERATE, 0, 0, &prop)) {
return JS_FALSE;
return false;
}
shape = (Shape *) prop;
}
@ -1037,6 +1073,13 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsid id,
intN shortid;
if (pobj->isNative()) {
if (shape->isMethod()) {
Value method = ObjectValue(shape->methodObject());
shape = pobj->methodReadBarrier(cx, *shape, &method);
if (!shape)
return false;
}
valroot.set(pobj->containsSlot(shape->slot)
? pobj->nativeGetSlot(shape->slot)
: UndefinedValue());
@ -1048,7 +1091,7 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsid id,
} else {
if (!pobj->getProperty(cx, propid, valroot.addr()) ||
!pobj->getAttributes(cx, propid, &attrs)) {
return JS_FALSE;
return false;
}
getter = NULL;
setter = NULL;
@ -1060,7 +1103,7 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsid id,
if (!js_DefineNativeProperty(cx, obj, propid, valroot.value(),
getter, setter, attrs, flags,
shortid, &prop)) {
return JS_FALSE;
return false;
}
shape = (Shape *) prop;
}
@ -1075,7 +1118,7 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsid id,
DBG_UNLOCK(rt);
wp = (JSWatchPoint *) cx->malloc(sizeof *wp);
if (!wp)
return JS_FALSE;
return false;
wp->handler = NULL;
wp->closure = NULL;
wp->object = obj;
@ -1088,7 +1131,7 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsid id,
JS_INIT_CLIST(&wp->links);
DBG_LOCK(rt);
DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
return JS_FALSE;
return false;
}
/*
@ -1111,13 +1154,15 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsid id,
wp->handler = handler;
wp->closure = reinterpret_cast<JSObject*>(closure);
DBG_UNLOCK(rt);
return JS_TRUE;
return true;
}
JS_PUBLIC_API(JSBool)
JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsid id,
JSWatchPointHandler *handlerp, JSObject **closurep)
{
assertSameCompartment(cx, obj);
JSRuntime *rt;
JSWatchPoint *wp;
@ -1145,6 +1190,8 @@ JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsid id,
JS_PUBLIC_API(JSBool)
JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj)
{
assertSameCompartment(cx, obj);
JSRuntime *rt;
JSWatchPoint *wp, *next;
uint32 sample;
@ -2054,6 +2101,7 @@ static JSFunctionSpec profiling_functions[] = {
JS_PUBLIC_API(JSBool)
JS_DefineProfilingFunctions(JSContext *cx, JSObject *obj)
{
assertSameCompartment(cx, obj);
#ifdef MOZ_PROFILING
return JS_DefineFunctions(cx, obj, profiling_functions);
#else
@ -2238,7 +2286,6 @@ js_ResumeVtune(JSContext *cx, uintN argc, jsval *vp)
#else
#include <sys/time.h>
#endif
#include "jstracer.h"
#define ETHOGRAM_BUF_SIZE 65536
@ -2671,3 +2718,20 @@ js_ShutdownEthogram(JSContext *cx, uintN argc, jsval *vp)
}
#endif /* MOZ_TRACEVIS */
#ifdef MOZ_TRACE_JSCALLS
JS_PUBLIC_API(void)
JS_SetFunctionCallback(JSContext *cx, JSFunctionCallback fcb)
{
cx->functionCallback = fcb;
}
JS_PUBLIC_API(JSFunctionCallback)
JS_GetFunctionCallback(JSContext *cx)
{
return cx->functionCallback;
}
#endif /* MOZ_TRACE_JSCALLS */

Просмотреть файл

@ -1,4 +1,4 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=99:
*
* ***** BEGIN LICENSE BLOCK *****
@ -49,6 +49,42 @@
JS_BEGIN_EXTERN_C
extern JS_PUBLIC_API(JSCrossCompartmentCall *)
JS_EnterCrossCompartmentCallScript(JSContext *cx, JSScript *target);
#ifdef __cplusplus
JS_END_EXTERN_C
namespace JS {
class JS_PUBLIC_API(AutoEnterScriptCompartment)
{
JSCrossCompartmentCall *call;
public:
AutoEnterScriptCompartment() : call(NULL) {}
bool enter(JSContext *cx, JSScript *target);
bool entered() const { return call != NULL; }
~AutoEnterScriptCompartment() {
if (call && call != reinterpret_cast<JSCrossCompartmentCall*>(1))
JS_LeaveCrossCompartmentCall(call);
}
};
} /* namespace JS */
JS_BEGIN_EXTERN_C
#endif
extern JS_PUBLIC_API(JSScript *)
JS_GetScriptFromObject(JSObject *scriptObject);
extern JS_PUBLIC_API(JSString *)
JS_DecompileScript(JSContext *cx, JSScript *script, const char *name, uintN indent);
/*
* Currently, we only support runtime-wide debugging. In the future, we should
* be able to support compartment-wide debugging.
@ -148,8 +184,8 @@ JS_ClearAllWatchPoints(JSContext *cx);
* Hide these non-API function prototypes by testing whether the internal
* header file "jsversion.h" has been included.
*/
extern void
js_TraceWatchPoints(JSTracer *trc, JSObject *obj);
extern JSBool
js_TraceWatchPoints(JSTracer *trc);
extern void
js_SweepWatchPoints(JSContext *cx);
@ -563,6 +599,28 @@ extern JS_FRIEND_API(JSBool)
js_ShutdownEthogram(JSContext *cx, uintN argc, jsval *vp);
#endif /* MOZ_TRACEVIS */
#ifdef MOZ_TRACE_JSCALLS
typedef void (*JSFunctionCallback)(const JSFunction *fun,
const JSScript *scr,
const JSContext *cx,
int entering);
/*
* The callback is expected to be quick and noninvasive. It should not
* trigger interrupts, turn on debugging, or produce uncaught JS
* exceptions. The state of the stack and registers in the context
* cannot be relied upon, since this callback may be invoked directly
* from either JIT. The 'entering' field means we are entering a
* function if it is positive, leaving a function if it is zero or
* negative.
*/
extern JS_PUBLIC_API(void)
JS_SetFunctionCallback(JSContext *cx, JSFunctionCallback fcb);
extern JS_PUBLIC_API(JSFunctionCallback)
JS_GetFunctionCallback(JSContext *cx);
#endif /* MOZ_TRACE_JSCALLS */
JS_END_EXTERN_C
#endif /* jsdbgapi_h___ */

Просмотреть файл

@ -1956,6 +1956,15 @@ EmitEnterBlock(JSContext *cx, JSParseNode *pn, JSCodeGenerator *cg)
blockObj->setSlot(slot, BooleanValue(isClosed));
}
/*
* If clones of this block will have any extensible parents, then the clones
* must get unique shapes; see the comments for js::Bindings::
* extensibleParents.
*/
if ((cg->flags & TCF_FUN_EXTENSIBLE_SCOPE) ||
cg->bindings.extensibleParents())
blockObj->setBlockOwnShape(cx);
return true;
}
@ -4572,7 +4581,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
}
#endif
fun = (JSFunction *) pn->pn_funbox->object;
fun = pn->pn_funbox->function();
JS_ASSERT(FUN_INTERPRETED(fun));
if (fun->u.i.script) {
/*

Просмотреть файл

@ -266,6 +266,16 @@ struct JSStmtInfo {
*/
#define TCF_IN_WITH 0x10000000
/*
* This function does something that can extend the set of bindings in its
* call objects --- it does a direct eval in non-strict code, or includes a
* function statement (as opposed to a function definition).
*
* This flag is *not* inherited by enclosed or enclosing functions; it
* applies only to the function in whose flags it appears.
*/
#define TCF_FUN_EXTENSIBLE_SCOPE 0x20000000
/*
* Flags to check for return; vs. return expr; in a function.
*/
@ -284,7 +294,8 @@ struct JSStmtInfo {
TCF_FUN_CALLS_EVAL | \
TCF_FUN_MIGHT_ALIAS_LOCALS | \
TCF_FUN_MUTATES_PARAMETER | \
TCF_STRICT_MODE_CODE)
TCF_STRICT_MODE_CODE | \
TCF_FUN_EXTENSIBLE_SCOPE)
struct JSTreeContext { /* tree context for semantic checks */
uint32 flags; /* statement state flags, see above */
@ -348,8 +359,9 @@ struct JSTreeContext { /* tree context for semantic checks */
JSTreeContext(js::Parser *prs)
: flags(0), bodyid(0), blockidGen(0), topStmt(NULL), topScopeStmt(NULL),
blockChainBox(NULL), blockNode(NULL), parser(prs), scopeChain_(NULL), parent(prs->tc),
staticLevel(0), funbox(NULL), functionList(NULL), innermostWith(NULL), bindings(prs->context),
blockChainBox(NULL), blockNode(NULL), parser(prs), scopeChain_(NULL),
parent(prs->tc), staticLevel(0), funbox(NULL), functionList(NULL),
innermostWith(NULL), bindings(prs->context, prs->emptyCallShape),
sharpSlotBase(-1)
{
prs->tc = this;
@ -456,6 +468,14 @@ struct JSTreeContext { /* tree context for semantic checks */
bool needsEagerArguments() const {
return inStrictMode() && ((usesArguments() && mutatesParameter()) || callsEval());
}
void noteHasExtensibleScope() {
flags |= TCF_FUN_EXTENSIBLE_SCOPE;
}
bool hasExtensibleScope() const {
return flags & TCF_FUN_EXTENSIBLE_SCOPE;
}
};
/*

Просмотреть файл

@ -51,7 +51,6 @@
#include "jsapi.h"
#include "jscntxt.h"
#include "jsversion.h"
#include "jsdbgapi.h"
#include "jsexn.h"
#include "jsfun.h"
#include "jsinterp.h"
@ -89,7 +88,7 @@ exn_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
Class js_ErrorClass = {
js_Error_str,
JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_MARK_IS_TRACE |
JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
JSCLASS_HAS_CACHED_PROTO(JSProto_Error),
PropertyStub, /* addProperty */
PropertyStub, /* delProperty */
@ -105,7 +104,7 @@ Class js_ErrorClass = {
NULL, /* construct */
NULL, /* xdrObject */
NULL, /* hasInstance */
JS_CLASS_TRACE(exn_trace)
exn_trace
};
typedef struct JSStackTraceElem {
@ -474,6 +473,15 @@ exn_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
atom = cx->runtime->atomState.messageAtom;
if (str == ATOM_TO_STRING(atom)) {
prop = js_message_str;
/*
* Per ES5 15.11.1.1, if Error is called with no argument or with
* undefined as the argument, it returns an Error object with no
* own message property.
*/
if (!priv->message)
return true;
v = STRING_TO_JSVAL(priv->message);
goto define;
}
@ -496,7 +504,7 @@ exn_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
if (str == ATOM_TO_STRING(atom)) {
stack = StackTraceToString(cx, priv);
if (!stack)
return JS_FALSE;
return false;
/* Allow to GC all things that were used to build stack trace. */
priv->stackDepth = 0;
@ -505,13 +513,13 @@ exn_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
goto define;
}
}
return JS_TRUE;
return true;
define:
if (!JS_DefineProperty(cx, obj, prop, v, NULL, NULL, JSPROP_ENUMERATE))
return JS_FALSE;
return false;
*objp = obj;
return JS_TRUE;
return true;
}
JSErrorReport *
@ -736,13 +744,13 @@ Exception(JSContext *cx, uintN argc, Value *vp)
/* Set the 'message' property. */
Value *argv = vp + 2;
if (argc != 0) {
if (argc != 0 && !argv[0].isUndefined()) {
message = js_ValueToString(cx, argv[0]);
if (!message)
return JS_FALSE;
argv[0].setString(message);
} else {
message = cx->runtime->emptyString;
message = NULL;
}
/* Set the 'fileName' property. */
@ -1054,7 +1062,7 @@ js_InitExceptionClasses(JSContext *cx, JSObject *obj)
/* Add properties to the prototype. */
JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_DECLARING);
if (!js_DefineNativeProperty(cx, proto, nameId, StringValue(atom),
PropertyStub, StrictPropertyStub,
PropertyStub, StrictPropertyStub,
JSPROP_ENUMERATE, 0, 0, NULL) ||
!js_DefineNativeProperty(cx, proto, messageId, empty,
PropertyStub, StrictPropertyStub,

Просмотреть файл

@ -53,7 +53,6 @@
#include "jsbuiltins.h"
#include "jscntxt.h"
#include "jsversion.h"
#include "jsdbgapi.h"
#include "jsemit.h"
#include "jsfun.h"
#include "jsgc.h"
@ -189,6 +188,11 @@ NewArguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject &callee)
if (!argsobj)
return NULL;
EmptyShape *emptyArgumentsShape = EmptyShape::getEmptyArgumentsShape(cx);
if (!emptyArgumentsShape)
return NULL;
AutoShapeRooter shapeRoot(cx, emptyArgumentsShape);
ArgumentsData *data = (ArgumentsData *)
cx->malloc(offsetof(ArgumentsData, slots) + argc * sizeof(Value));
if (!data)
@ -201,7 +205,7 @@ NewArguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject &callee)
: &js_ArgumentsClass,
proto, parent, NULL, false);
argsobj->setMap(cx->compartment->emptyArgumentsShape);
argsobj->setMap(emptyArgumentsShape);
argsobj->setArgsLength(argc);
argsobj->setArgsData(data);
@ -496,6 +500,7 @@ WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSFunction *fun)
/* Deoptimize wfun from FUN_{FLAT,NULL}_CLOSURE to FUN_INTERPRETED. */
FUN_SET_KIND(wfun, JSFUN_INTERPRETED);
wfun->u.i.script = wscript;
js_CallNewScriptHook(cx, wscript, wfun);
return wfunobj;
}
@ -833,7 +838,7 @@ Class js_ArgumentsClass = {
"Arguments",
JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
JSCLASS_HAS_RESERVED_SLOTS(JSObject::ARGS_CLASS_RESERVED_SLOTS) |
JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
PropertyStub, /* addProperty */
args_delProperty,
PropertyStub, /* getProperty */
@ -848,7 +853,7 @@ Class js_ArgumentsClass = {
NULL, /* construct */
NULL, /* xdrObject */
NULL, /* hasInstance */
JS_CLASS_TRACE(args_trace)
args_trace
};
namespace js {
@ -862,7 +867,7 @@ Class StrictArgumentsClass = {
"Arguments",
JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
JSCLASS_HAS_RESERVED_SLOTS(JSObject::ARGS_CLASS_RESERVED_SLOTS) |
JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
PropertyStub, /* addProperty */
args_delProperty,
PropertyStub, /* getProperty */
@ -877,7 +882,7 @@ Class StrictArgumentsClass = {
NULL, /* construct */
NULL, /* xdrObject */
NULL, /* hasInstance */
JS_CLASS_TRACE(args_trace)
args_trace
};
}
@ -959,8 +964,7 @@ NewCallObject(JSContext *cx, Bindings *bindings, JSObject &scopeChain, JSObject
return NULL;
/* Init immediately to avoid GC seeing a half-init'ed object. */
callobj->init(cx, &js_CallClass, NULL, &scopeChain, NULL, false);
callobj->setMap(bindings->lastShape());
callobj->initCall(cx, bindings, &scopeChain);
/* This must come after callobj->lastProp has been set. */
if (!callobj->ensureInstanceReservedSlots(cx, argsVars))
@ -989,8 +993,12 @@ NewDeclEnvObject(JSContext *cx, JSStackFrame *fp)
if (!envobj)
return NULL;
EmptyShape *emptyDeclEnvShape = EmptyShape::getEmptyDeclEnvShape(cx);
if (!emptyDeclEnvShape)
return NULL;
envobj->init(cx, &js_DeclEnvClass, NULL, &fp->scopeChain(), fp, false);
envobj->setMap(cx->compartment->emptyDeclEnvShape);
envobj->setMap(emptyDeclEnvShape);
return envobj;
}
@ -1395,7 +1403,7 @@ JS_PUBLIC_DATA(Class) js_CallClass = {
"Call",
JSCLASS_HAS_PRIVATE |
JSCLASS_HAS_RESERVED_SLOTS(JSObject::CALL_RESERVED_SLOTS) |
JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS | JSCLASS_MARK_IS_TRACE,
JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS,
PropertyStub, /* addProperty */
PropertyStub, /* delProperty */
PropertyStub, /* getProperty */
@ -1410,7 +1418,7 @@ JS_PUBLIC_DATA(Class) js_CallClass = {
NULL, /* construct */
NULL, /* xdrObject */
NULL, /* hasInstance */
JS_CLASS_TRACE(call_trace)
call_trace
};
bool
@ -1636,10 +1644,7 @@ fun_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
break;
default:
/* XXX fun[0] and fun.arguments[0] are equivalent. */
if (fp && fp->isFunctionFrame() && uint16(slot) < fp->numFormalArgs())
*vp = fp->formalArg(slot);
break;
JS_NOT_REACHED("fun_getProperty");
}
return true;
@ -1898,7 +1903,7 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
fun->u.i.wrapper = JSPackedBool((firstword >> 1) & 1);
}
if (!js_XDRScript(xdr, &fun->u.i.script, NULL))
if (!js_XDRScriptAndSubscripts(xdr, &fun->u.i.script))
return false;
if (xdr->mode == JSXDR_DECODE) {
@ -2009,7 +2014,7 @@ JS_PUBLIC_DATA(Class) js_FunctionClass = {
js_Function_str,
JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
JSCLASS_HAS_RESERVED_SLOTS(JSFunction::CLASS_RESERVED_SLOTS) |
JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
PropertyStub, /* addProperty */
PropertyStub, /* delProperty */
PropertyStub, /* getProperty */
@ -2022,9 +2027,9 @@ JS_PUBLIC_DATA(Class) js_FunctionClass = {
NULL, /* checkAccess */
NULL, /* call */
NULL, /* construct */
js_XDRFunctionObject,
NULL,
fun_hasInstance,
JS_CLASS_TRACE(fun_trace)
fun_trace
};
JSString *
@ -2044,18 +2049,28 @@ fun_toStringHelper(JSContext *cx, JSObject *obj, uintN indent)
if (!fun)
return NULL;
if (!indent) {
ToSourceCache::Ptr p = cx->compartment->toSourceCache.lookup(fun);
if (!indent && !cx->compartment->toSourceCache.empty()) {
ToSourceCache::Ptr p = cx->compartment->toSourceCache.ref().lookup(fun);
if (p)
return p->value;
}
JSString *str = JS_DecompileFunction(cx, fun, indent);
if (!str)
return false;
return NULL;
if (!indent)
cx->compartment->toSourceCache.put(fun, str);
if (!indent) {
LazilyConstructed<ToSourceCache> &lazy = cx->compartment->toSourceCache;
if (lazy.empty()) {
lazy.construct();
if (!lazy.ref().init())
return NULL;
}
if (!lazy.ref().put(fun, str))
return NULL;
}
return str;
}
@ -2461,7 +2476,12 @@ Function(JSContext *cx, uintN argc, Value *vp)
return JS_FALSE;
}
Bindings bindings(cx);
EmptyShape *emptyCallShape = EmptyShape::getEmptyCallShape(cx);
if (!emptyCallShape)
return JS_FALSE;
AutoShapeRooter shapeRoot(cx, emptyCallShape);
Bindings bindings(cx, emptyCallShape);
AutoBindingsRooter root(cx, bindings);
Value *argv = vp + 2;
@ -2638,7 +2658,7 @@ Function(JSContext *cx, uintN argc, Value *vp)
namespace js {
JS_FRIEND_API(bool)
bool
IsBuiltinFunctionConstructor(JSFunction *fun)
{
return fun->maybeNative() == Function;
@ -2701,6 +2721,7 @@ js_InitFunctionClass(JSContext *cx, JSObject *obj)
script->owner = NULL;
#endif
fun->u.i.script = script;
js_CallNewScriptHook(cx, script, fun);
if (obj->isGlobal()) {
/* ES5 13.2.3: Construct the unique [[ThrowTypeError]] function object. */

Просмотреть файл

@ -96,7 +96,7 @@
JSFunctionSpec::call points to a
JSNativeTraceInfo. */
#define JSFUN_INTERPRETED 0x4000 /* use u.i if kind >= this value else u.n */
#define JSFUN_FLAT_CLOSURE 0x8000 /* flag (aka "display") closure */
#define JSFUN_FLAT_CLOSURE 0x8000 /* flat (aka "display") closure */
#define JSFUN_NULL_CLOSURE 0xc000 /* null closure entrains no scope chain */
#define JSFUN_KINDMASK 0xc000 /* encode interp vs. native and closure
optimization level -- see above */
@ -429,7 +429,7 @@ GetFunctionNameBytes(JSContext *cx, JSFunction *fun, JSAutoByteString *bytes)
return js_anonymous_str;
}
extern JS_FRIEND_API(bool)
extern bool
IsBuiltinFunctionConstructor(JSFunction *fun);
/*

Просмотреть файл

@ -78,7 +78,6 @@
#include "jsscript.h"
#include "jsstaticcheck.h"
#include "jsstr.h"
#include "jstracer.h"
#include "methodjit/MethodJIT.h"
#if JS_HAS_XML_SUPPORT
@ -171,8 +170,6 @@ Arena<T>::init(JSCompartment *compartment, unsigned thingKind)
aheader.compartment = compartment;
aheader.thingKind = thingKind;
aheader.freeList = &t.things[0].cell;
aheader.thingSize = sizeof(T);
aheader.isUsed = true;
JS_ASSERT(sizeof(T) == sizeof(ThingOrCell<T>));
ThingOrCell<T> *thing = &t.things[0];
ThingOrCell<T> *last = &t.things[JS_ARRAY_LENGTH(t.things) - 1];
@ -182,6 +179,8 @@ Arena<T>::init(JSCompartment *compartment, unsigned thingKind)
}
last->cell.link = NULL;
#ifdef DEBUG
aheader.thingSize = sizeof(T);
aheader.isUsed = true;
aheader.hasFreeThings = true;
#endif
}
@ -212,16 +211,15 @@ template<typename T>
inline ConservativeGCTest
Arena<T>::mark(T *thing, JSTracer *trc)
{
JS_ASSERT(sizeof(T) == aheader.thingSize);
T *alignedThing = getAlignedThing(thing);
if (alignedThing > &t.things[ThingsPerArena-1].t || alignedThing < &t.things[0].t)
return CGCT_NOTARENA;
if (!aheader.isUsed || inFreeList(alignedThing))
if (!aheader.compartment || inFreeList(alignedThing))
return CGCT_NOTLIVE;
JS_ASSERT(sizeof(T) == aheader.thingSize);
JS_SET_TRACING_NAME(trc, "machine stack");
Mark(trc, alignedThing);
@ -291,11 +289,17 @@ Chunk::init(JSRuntime *rt)
Arena<FreeCell> *last = &arenas[JS_ARRAY_LENGTH(arenas) - 1];
while (arena < last) {
arena->header()->next = arena + 1;
arena->header()->compartment = NULL;
#ifdef DEBUG
arena->header()->isUsed = false;
#endif
++arena;
}
last->header()->next = NULL;
last->header()->compartment = NULL;
#ifdef DEBUG
last->header()->isUsed = false;
#endif
info.numFree = ArenasPerChunk;
}
@ -354,7 +358,10 @@ Chunk::releaseArena(Arena<T> *arena)
rt->gcBytes -= sizeof(Arena<T>);
comp->gcBytes -= sizeof(Arena<T>);
info.emptyArenaLists.insert((Arena<Cell> *)arena);
#ifdef DEBUG
arena->header()->isUsed = false;
#endif
arena->header()->compartment = NULL;
++info.numFree;
if (unused())
info.age = 0;
@ -633,9 +640,6 @@ MarkIfGCThingWord(JSTracer *trc, jsuword w, uint32 &thingKind)
ArenaHeader *aheader = cell->arena()->header();
if (!aheader->isUsed)
return CGCT_FREEARENA;
ConservativeGCTest test;
thingKind = aheader->thingKind;
@ -1205,11 +1209,6 @@ RefillFinalizableFreeList(JSContext *cx, unsigned thingKind)
}
}
intN
js_GetExternalStringGCType(JSString *str) {
return GetExternalStringGCType((JSExternalString *)str);
}
uint32
js_GetGCThingTraceKind(void *thing) {
return GetGCThingTraceKind(thing);
@ -1544,7 +1543,6 @@ AutoGCRooter::trace(JSTracer *trc)
MarkValue(trc, desc.value, "PropDesc::value");
MarkValue(trc, desc.get, "PropDesc::get");
MarkValue(trc, desc.set, "PropDesc::set");
MarkId(trc, desc.id, "PropDesc::id");
}
return;
}
@ -1635,9 +1633,6 @@ MarkContext(JSTracer *trc, JSContext *acx)
js_TraceSharpMap(trc, &acx->sharpObjectMap);
MarkValue(trc, acx->iterValue, "iterValue");
if (acx->compartment)
acx->compartment->mark(trc);
}
JS_REQUIRES_STACK void
@ -1726,8 +1721,6 @@ MarkRuntime(JSTracer *trc)
while (JSContext *acx = js_ContextIterator(rt, JS_TRUE, &iter))
MarkContext(trc, acx);
rt->atomsCompartment->mark(trc);
#ifdef JS_TRACER
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
(*c)->traceMonitor.mark(trc);
@ -1871,7 +1864,8 @@ js_FinalizeStringRT(JSRuntime *rt, JSString *str)
JS_RUNTIME_UNMETER(rt, liveDependentStrings);
} else {
unsigned thingKind = str->asCell()->arena()->header()->thingKind;
JS_ASSERT(IsFinalizableStringKind(thingKind));
JS_ASSERT(unsigned(FINALIZE_SHORT_STRING) <= thingKind &&
thingKind <= unsigned(FINALIZE_EXTERNAL_STRING));
/* A stillborn string has null chars, so is not valid. */
jschar *chars = const_cast<jschar *>(str->flatChars());
@ -2189,13 +2183,8 @@ SweepCompartments(JSContext *cx, JSGCInvocationKind gckind)
while (read < end) {
JSCompartment *compartment = *read++;
/*
* Unmarked compartments containing marked objects don't get deleted,
* except when LAST_CONTEXT GC is performed.
*/
if ((!compartment->isMarked() && compartment->arenaListsAreEmpty()) ||
gckind == GC_LAST_CONTEXT)
{
if (!compartment->hold &&
(compartment->arenaListsAreEmpty() || gckind == GC_LAST_CONTEXT)) {
JS_ASSERT(compartment->freeLists.isEmpty());
if (callback)
(void) callback(cx, compartment, JSCOMPARTMENT_DESTROY);
@ -2270,7 +2259,6 @@ MarkAndSweepCompartment(JSContext *cx, JSCompartment *comp, JSGCInvocationKind g
JS_ASSERT(!rt->gcRegenShapes);
JS_ASSERT(gckind != GC_LAST_CONTEXT);
JS_ASSERT(comp != rt->atomsCompartment);
JS_ASSERT(!comp->isMarked());
JS_ASSERT(comp->rt->gcMode == JSGC_MODE_COMPARTMENT);
/*
@ -2286,9 +2274,7 @@ MarkAndSweepCompartment(JSContext *cx, JSCompartment *comp, JSGCInvocationKind g
r.front()->clearMarkBitmap();
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
(*c)->markCrossCompartment(&gcmarker);
comp->mark(&gcmarker);
(*c)->markCrossCompartmentWrappers(&gcmarker);
MarkRuntime(&gcmarker);
@ -2298,6 +2284,15 @@ MarkAndSweepCompartment(JSContext *cx, JSCompartment *comp, JSGCInvocationKind g
*/
gcmarker.markDelayedChildren();
/*
* Mark weak roots.
*/
while (true) {
if (!js_TraceWatchPoints(&gcmarker))
break;
gcmarker.markDelayedChildren();
}
rt->gcMarkingTracer = NULL;
if (rt->gcCallback)
@ -2317,7 +2312,7 @@ MarkAndSweepCompartment(JSContext *cx, JSCompartment *comp, JSGCInvocationKind g
#ifdef DEBUG
/* Make sure that we didn't mark an object in another compartment */
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
JS_ASSERT_IF(*c != comp, checkArenaListAllUnmarked(*c));
JS_ASSERT_IF(*c != comp && *c != rt->atomsCompartment, checkArenaListAllUnmarked(*c));
#endif
/*
@ -2377,8 +2372,6 @@ MarkAndSweepCompartment(JSContext *cx, JSCompartment *comp, JSGCInvocationKind g
ExpireGCChunks(rt);
TIMESTAMP(sweepDestroyEnd);
comp->clearMark();
if (rt->gcCallback)
(void) rt->gcCallback(cx, JSGC_FINALIZE_END);
}
@ -2417,6 +2410,15 @@ MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind GCTIMER_PARAM)
*/
gcmarker.markDelayedChildren();
/*
* Mark weak roots.
*/
while (true) {
if (!js_TraceWatchPoints(&gcmarker))
break;
gcmarker.markDelayedChildren();
}
rt->gcMarkingTracer = NULL;
if (rt->gcCallback)
@ -2507,9 +2509,6 @@ MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind GCTIMER_PARAM)
ExpireGCChunks(rt);
TIMESTAMP(sweepDestroyEnd);
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
(*c)->clearMark();
if (rt->gcCallback)
(void) rt->gcCallback(cx, JSGC_FINALIZE_END);
#ifdef DEBUG_srcnotesize
@ -2720,14 +2719,11 @@ GCUntilDone(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM
AutoGCSession gcsession(cx);
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
JS_ASSERT(!(*c)->isMarked());
/*
* We should not be depending on cx->compartment in the GC, so set it to
* NULL to look for violations.
*/
SwitchToCompartment(cx, (JSCompartment *)NULL);
SwitchToCompartment sc(cx, (JSCompartment *)NULL);
JS_ASSERT(!rt->gcCurrentCompartment);
rt->gcCurrentCompartment = comp;
@ -2842,6 +2838,18 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
namespace js {
namespace gc {
void
MarkObjectSlots(JSTracer *trc, JSObject *obj)
{
JS_ASSERT(obj->slotSpan() <= obj->numSlots());
uint32 nslots = obj->slotSpan();
for (uint32 i = 0; i != nslots; ++i) {
const Value &v = obj->getSlot(i);
JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i);
MarkValueRaw(trc, v);
}
}
bool
SetProtoCheckingForCycles(JSContext *cx, JSObject *obj, JSObject *proto)
{

Просмотреть файл

@ -83,10 +83,7 @@ struct Shape;
namespace gc {
/*
* The kind of GC thing with a finalizer. The external strings follow the
* ordinary string to simplify js_GetExternalStringGCType.
*/
/* The kind of GC thing with a finalizer. */
enum FinalizeKind {
FINALIZE_OBJECT0,
FINALIZE_OBJECT2,
@ -113,9 +110,9 @@ struct ArenaHeader {
Arena<FreeCell> *next;
FreeCell *freeList;
unsigned thingKind;
bool isUsed;
size_t thingSize;
#ifdef DEBUG
size_t thingSize;
bool isUsed;
bool hasFreeThings;
#endif
};
@ -304,7 +301,9 @@ EmptyArenaLists::getNext(JSCompartment *comp, unsigned thingKind) {
if (arena) {
JS_ASSERT(arena->header()->isUsed == false);
JS_ASSERT(arena->header()->thingSize == sizeof(T));
#ifdef DEBUG
arena->header()->isUsed = true;
#endif
arena->header()->thingKind = thingKind;
arena->header()->compartment = comp;
return arena;
@ -433,7 +432,7 @@ Arena<T>::getAlignedThing(void *thing)
{
jsuword start = reinterpret_cast<jsuword>(&t.things[0]);
jsuword offset = reinterpret_cast<jsuword>(thing) - start;
offset -= offset % aheader.thingSize;
offset -= offset % sizeof(T);
return reinterpret_cast<T *>(start + offset);
}
@ -533,28 +532,6 @@ GetFinalizableTraceKind(size_t thingKind)
return map[thingKind];
}
static inline bool
IsFinalizableStringKind(unsigned thingKind)
{
return unsigned(FINALIZE_SHORT_STRING) <= thingKind &&
thingKind <= unsigned(FINALIZE_EXTERNAL_STRING);
}
/*
* Get the type of the external string or -1 if the string was not created
* with JS_NewExternalString.
*/
static inline intN
GetExternalStringGCType(JSExternalString *str)
{
JS_STATIC_ASSERT(FINALIZE_STRING + 1 == FINALIZE_EXTERNAL_STRING);
JS_ASSERT(!JSString::isStatic(str));
unsigned thingKind = str->externalStringType;
JS_ASSERT(IsFinalizableStringKind(thingKind));
return intN(thingKind);
}
static inline uint32
GetGCThingTraceKind(void *thing)
{
@ -746,13 +723,6 @@ extern bool
CheckAllocation(JSContext *cx);
#endif
/*
* Get the type of the external string or -1 if the string was not created
* with JS_NewExternalString.
*/
extern intN
js_GetExternalStringGCType(JSString *str);
extern JS_FRIEND_API(uint32)
js_GetGCThingTraceKind(void *thing);

Просмотреть файл

@ -263,6 +263,9 @@ MarkObject(JSTracer *trc, JSObject &obj, const char *name)
Mark(trc, &obj);
}
void
MarkObjectSlots(JSTracer *trc, JSObject *obj);
static inline void
MarkChildren(JSTracer *trc, JSObject *obj)
{
@ -284,9 +287,20 @@ MarkChildren(JSTracer *trc, JSObject *obj)
}
}
/* Delegate to ops or the native marking op. */
TraceOp op = obj->getOps()->trace;
(op ? op : js_TraceObject)(trc, obj);
Class *clasp = obj->getClass();
if (clasp->trace)
clasp->trace(trc, obj);
if (obj->isNative()) {
#ifdef JS_DUMP_SCOPE_METERS
js::MeterEntryCount(obj->propertyCount);
#endif
obj->trace(trc);
if (obj->slotSpan() > 0)
MarkObjectSlots(trc, obj);
}
}
static inline void
@ -401,36 +415,15 @@ Untag(JSString *str)
}
static JS_ALWAYS_INLINE void
NonRopeTypedMarker(JSRuntime *rt, JSString *str)
NonRopeTypedMarker(JSString *str)
{
/* N.B. The base of a dependent string is not necessarily flat. */
JS_ASSERT(!str->isRope());
if (rt->gcCurrentCompartment) {
for (;;) {
if (JSString::isStatic(str))
break;
/*
* If we perform single-compartment GC don't mark Strings outside the current compartment.
* Dependent Strings are not shared between compartments and they can't be in the atomsCompartment.
*/
if (str->asCell()->compartment() != rt->gcCurrentCompartment) {
JS_ASSERT(str->asCell()->compartment() == rt->atomsCompartment);
break;
}
if (!str->asCell()->markIfUnmarked())
break;
if (!str->isDependent())
break;
str = str->dependentBase();
}
} else {
while (!JSString::isStatic(str) &&
str->asCell()->markIfUnmarked() &&
str->isDependent()) {
str = str->dependentBase();
}
while (!JSString::isStatic(str) &&
str->asCell()->markIfUnmarked() &&
str->isDependent()) {
str = str->dependentBase();
}
}
@ -442,13 +435,10 @@ static JS_ALWAYS_INLINE void
TypedMarker(JSTracer *trc, JSString *str)
{
using namespace detail;
JSRuntime *rt = trc->context->runtime;
JS_ASSERT(!JSString::isStatic(str));
#ifdef DEBUG
JSCompartment *strComp = str->asCell()->compartment();
#endif
if (!str->isRope()) {
NonRopeTypedMarker(rt, str);
NonRopeTypedMarker(str);
return;
}
@ -460,7 +450,6 @@ TypedMarker(JSTracer *trc, JSString *str)
*/
JSString *parent = NULL;
first_visit_node: {
JS_ASSERT(strComp == str->asCell()->compartment() || str->asCell()->compartment() == rt->atomsCompartment);
JS_ASSERT(!JSString::isStatic(str));
if (!str->asCell()->markIfUnmarked())
goto finish_node;
@ -472,10 +461,7 @@ TypedMarker(JSTracer *trc, JSString *str)
str = left;
goto first_visit_node;
}
JS_ASSERT_IF(!JSString::isStatic(left),
strComp == left->asCell()->compartment()
|| left->asCell()->compartment() == rt->atomsCompartment);
NonRopeTypedMarker(rt, left);
NonRopeTypedMarker(left);
}
visit_right_child: {
JSString *right = str->ropeRight();
@ -486,10 +472,7 @@ TypedMarker(JSTracer *trc, JSString *str)
str = right;
goto first_visit_node;
}
JS_ASSERT_IF(!JSString::isStatic(right),
strComp == right->asCell()->compartment()
|| right->asCell()->compartment() == rt->atomsCompartment);
NonRopeTypedMarker(rt, right);
NonRopeTypedMarker(right);
}
finish_node: {
if (!parent)

Просмотреть файл

@ -396,25 +396,27 @@ jsrefcount destroyChunkCount = 0;
GCTimer::GCTimer() {
getFirstEnter();
memset(this, 0, sizeof(GCTimer));
enter = rdtsc();
enter = PRMJ_Now();
}
uint64
GCTimer::getFirstEnter() {
static uint64 firstEnter = rdtsc();
static uint64 firstEnter = PRMJ_Now();
return firstEnter;
}
#define TIMEDIFF(start, end) ((double)(end - start) / PRMJ_USEC_PER_MSEC)
void
GCTimer::finish(bool lastGC) {
end = rdtsc();
end = PRMJ_Now();
if (startMark > 0) {
if (JS_WANT_GC_SUITE_PRINT) {
fprintf(stderr, "%f %f %f\n",
(double)(end - enter) / 1e6,
(double)(startSweep - startMark) / 1e6,
(double)(sweepDestroyEnd - startSweep) / 1e6);
TIMEDIFF(enter, end),
TIMEDIFF(startMark, startSweep),
TIMEDIFF(startSweep, sweepDestroyEnd));
} else {
static FILE *gcFile;
@ -425,17 +427,17 @@ GCTimer::finish(bool lastGC) {
fprintf(gcFile, " FinStr, SwShapes, Destroy, +Chunks, -Chunks\n");
}
JS_ASSERT(gcFile);
fprintf(gcFile, "%12.1f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, %8.1f, %6.1f, ",
(double)(enter - getFirstEnter()) / 1e6,
(double)(end - enter) / 1e6,
(double)(startSweep - startMark) / 1e6,
(double)(sweepDestroyEnd - startSweep) / 1e6,
(double)(sweepObjectEnd - startSweep) / 1e6,
(double)(sweepStringEnd - sweepObjectEnd) / 1e6,
(double)(sweepShapeEnd - sweepStringEnd) / 1e6,
(double)(sweepDestroyEnd - sweepShapeEnd) / 1e6);
fprintf(gcFile, "%7d, %7d \n", newChunkCount,
destroyChunkCount);
/* App , Tot , Mar , Swe , FiO , FiS , SwS , Des */
fprintf(gcFile, "%12.0f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, %8.1f, %6.1f, ",
TIMEDIFF(getFirstEnter(), enter),
TIMEDIFF(enter, end),
TIMEDIFF(startMark, startSweep),
TIMEDIFF(startSweep, sweepDestroyEnd),
TIMEDIFF(startSweep, sweepObjectEnd),
TIMEDIFF(sweepObjectEnd, sweepStringEnd),
TIMEDIFF(sweepStringEnd, sweepShapeEnd),
TIMEDIFF(sweepShapeEnd, sweepDestroyEnd));
fprintf(gcFile, "%7d, %7d \n", newChunkCount, destroyChunkCount);
fflush(gcFile);
if (lastGC) {
@ -448,6 +450,8 @@ GCTimer::finish(bool lastGC) {
destroyChunkCount = 0;
}
#undef TIMEDIFF
#endif
} //js

Просмотреть файл

@ -161,7 +161,7 @@ struct GCTimer {
# define GCTIMER_PARAM , GCTimer &gcTimer
# define GCTIMER_ARG , gcTimer
# define TIMESTAMP(x) (gcTimer.x = rdtsc())
# define TIMESTAMP(x) (gcTimer.x = PRMJ_Now())
# define GCTIMER_BEGIN() GCTimer gcTimer
# define GCTIMER_END(last) (gcTimer.finish(last))
#else

Просмотреть файл

@ -66,7 +66,6 @@
#include "jsobj.h"
#include "jsopcode.h"
#include "jspropertycache.h"
#include "jsscan.h"
#include "jsemit.h"
#include "jsscope.h"
#include "jsscript.h"
@ -114,21 +113,14 @@ jsbytecode *
JSStackFrame::pc(JSContext *cx, JSStackFrame *next)
{
JS_ASSERT_IF(next, next->prev_ == this);
JS_ASSERT(cx->containingSegment(this) != NULL);
JSFrameRegs *regs;
if (cx->regs) {
regs = cx->regs;
} else {
StackSegment *segment = cx->getCurrentSegment();
regs = segment->getSuspendedRegs();
}
if (this == regs->fp)
StackSegment *seg = cx->containingSegment(this);
JSFrameRegs *regs = seg->getCurrentRegs();
if (regs->fp == this)
return regs->pc;
if (!next)
next = cx->computeNextFrame(this);
next = seg->computeNextFrame(this);
if (next->flags_ & JSFRAME_HAS_PREVPC)
return next->prevpc_;
@ -803,7 +795,7 @@ InvokeSessionGuard::start(JSContext *cx, const Value &calleev, const Value &this
/* Cannot also cache the raw code pointer; it can change. */
/* Hoist dynamic checks from CheckStackAndEnterMethodJIT. */
JS_CHECK_RECURSION(cx, return JS_FALSE);
JS_CHECK_RECURSION(cx, return false);
stackLimit_ = stack.getStackLimit(cx);
if (!stackLimit_)
return false;
@ -898,7 +890,7 @@ ExternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, const Value &fval,
* ExternalInvoke could result in another try to get or set the same id
* again, see bug 355497.
*/
JS_CHECK_RECURSION(cx, return JS_FALSE);
JS_CHECK_RECURSION(cx, return false);
return ExternalInvoke(cx, ObjectValue(*obj), fval, argc, argv, rval);
}
@ -1315,17 +1307,16 @@ InvokeConstructorWithGivenThis(JSContext *cx, JSObject *thisobj, const Value &fv
}
bool
DirectEval(JSContext *cx, JSFunction *evalfun, uint32 argc, Value *vp)
DirectEval(JSContext *cx, uint32 argc, Value *vp)
{
JS_ASSERT(vp == cx->regs->sp - argc - 2);
JS_ASSERT(vp[0].isObject());
JS_ASSERT(vp[0].toObject().isFunction());
JS_ASSERT(vp[0].toObject().getFunctionPrivate() == evalfun);
JS_ASSERT(IsBuiltinEvalFunction(evalfun));
JSStackFrame *caller = cx->fp();
JS_ASSERT(caller->isScriptFrame());
AutoFunctionCallProbe callProbe(cx, evalfun, caller->script());
JS_ASSERT(IsBuiltinEvalForScope(&caller->scopeChain(), vp[0]));
AutoFunctionCallProbe callProbe(cx, vp[0].toObject().getFunctionPrivate(), caller->script());
JSObject *scopeChain =
GetScopeChainFast(cx, caller, JSOP_EVAL, JSOP_EVAL_LENGTH + JSOP_LINENO_LENGTH);
@ -2054,7 +2045,7 @@ AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, JSFrameRegs& regs,
}
if (!ok)
return false;
if (cx->runtime->gcNumber != sample || entry->vshape() != pobj->shape())
if (cx->runtime->gcNumber != sample)
return true;
JS_ASSERT(prop);
JS_ASSERT(pobj == found);
@ -2630,7 +2621,7 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte
} while (0););
#endif
#else
JS_CHECK_RECURSION(cx, return JS_FALSE);
JS_CHECK_RECURSION(cx, goto error);
#endif
#if JS_THREADED_INTERP
@ -4698,14 +4689,10 @@ BEGIN_CASE(JSOP_EVAL)
argc = GET_ARGC(regs.pc);
vp = regs.sp - (argc + 2);
if (!IsFunctionObject(*vp, &callee))
if (!IsBuiltinEvalForScope(&regs.fp->scopeChain(), *vp))
goto call_using_invoke;
newfun = callee->getFunctionPrivate();
if (!IsBuiltinEvalFunction(newfun))
goto call_using_invoke;
if (!DirectEval(cx, newfun, argc, vp))
if (!DirectEval(cx, argc, vp))
goto error;
}
END_CASE(JSOP_EVAL)

Просмотреть файл

@ -964,13 +964,11 @@ ExternalInvokeConstructor(JSContext *cx, const Value &fval, uintN argc, Value *a
/*
* Performs a direct eval for the given arguments, which must correspond to the
* currently-executing stack frame, which must be a script frame. evalfun must
* be the built-in eval function and must correspond to the callee in vp[0].
* When this function succeeds it returns the result in *vp, adjusts the JS
* stack pointer, and returns true.
* currently-executing stack frame, which must be a script frame. On completion
* the result is returned in *vp and the JS stack pointer is adjusted.
*/
extern JS_REQUIRES_STACK bool
DirectEval(JSContext *cx, JSFunction *evalfun, uint32 argc, Value *vp);
DirectEval(JSContext *cx, uint32 argc, Value *vp);
/*
* Performs a direct eval for the given arguments, which must correspond to the

Просмотреть файл

@ -110,6 +110,7 @@ JSStackFrame::resetInvokeCallFrame()
JSFRAME_HAS_RVAL |
JSFRAME_HAS_SCOPECHAIN |
JSFRAME_HAS_ANNOTATION |
JSFRAME_HAS_HOOK_DATA |
JSFRAME_FINISHED_IN_INTERPRETER)));
flags_ &= JSFRAME_FUNCTION |
JSFRAME_OVERFLOW_ARGS |

Просмотреть файл

@ -64,11 +64,9 @@
#include "jsobj.h"
#include "jsopcode.h"
#include "jsproxy.h"
#include "jsscan.h"
#include "jsscope.h"
#include "jsscript.h"
#include "jsstaticcheck.h"
#include "jstracer.h"
#include "jsvector.h"
#if JS_HAS_XML_SUPPORT
@ -89,8 +87,7 @@ static JSObject *iterator_iterator(JSContext *cx, JSObject *obj, JSBool keysonly
Class js_IteratorClass = {
"Iterator",
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator) |
JSCLASS_MARK_IS_TRACE,
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator),
PropertyStub, /* addProperty */
PropertyStub, /* delProperty */
PropertyStub, /* getProperty */
@ -105,7 +102,7 @@ Class js_IteratorClass = {
NULL, /* construct */
NULL, /* xdrObject */
NULL, /* hasInstance */
JS_CLASS_TRACE(iterator_trace),
iterator_trace,
{
NULL, /* equality */
NULL, /* outerObject */
@ -426,8 +423,13 @@ NewIteratorObject(JSContext *cx, uintN flags)
JSObject *obj = js_NewGCObject(cx, FINALIZE_OBJECT0);
if (!obj)
return false;
EmptyShape *emptyEnumeratorShape = EmptyShape::getEmptyEnumeratorShape(cx);
if (!emptyEnumeratorShape)
return NULL;
obj->init(cx, &js_IteratorClass, NULL, NULL, NULL, false);
obj->setMap(cx->compartment->emptyEnumeratorShape);
obj->setMap(emptyEnumeratorShape);
return obj;
}
@ -1100,7 +1102,7 @@ generator_trace(JSTracer *trc, JSObject *obj)
Class js_GeneratorClass = {
js_Generator_str,
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Generator) |
JSCLASS_IS_ANONYMOUS | JSCLASS_MARK_IS_TRACE,
JSCLASS_IS_ANONYMOUS,
PropertyStub, /* addProperty */
PropertyStub, /* delProperty */
PropertyStub, /* getProperty */
@ -1115,7 +1117,7 @@ Class js_GeneratorClass = {
NULL, /* construct */
NULL, /* xdrObject */
NULL, /* hasInstance */
JS_CLASS_TRACE(generator_trace),
generator_trace,
{
NULL, /* equality */
NULL, /* outerObject */
@ -1378,6 +1380,7 @@ generator_op(JSContext *cx, JSGeneratorOp op, Value *vp, uintN argc)
default:
JS_ASSERT(op == JSGENOP_CLOSE);
gen->state = JSGEN_CLOSED;
JS_SET_RVAL(cx, vp, UndefinedValue());
return JS_TRUE;
}
} else if (gen->state == JSGEN_CLOSED) {
@ -1391,6 +1394,7 @@ generator_op(JSContext *cx, JSGeneratorOp op, Value *vp, uintN argc)
return JS_FALSE;
default:
JS_ASSERT(op == JSGENOP_CLOSE);
JS_SET_RVAL(cx, vp, UndefinedValue());
return JS_TRUE;
}
}
@ -1398,7 +1402,8 @@ generator_op(JSContext *cx, JSGeneratorOp op, Value *vp, uintN argc)
bool undef = ((op == JSGENOP_SEND || op == JSGENOP_THROW) && argc != 0);
if (!SendToGenerator(cx, op, obj, gen, undef ? vp[2] : UndefinedValue()))
return JS_FALSE;
*vp = gen->floatingFrame()->returnValue();
JS_SET_RVAL(cx, vp, gen->floatingFrame()->returnValue());
return JS_TRUE;
}

Просмотреть файл

@ -74,16 +74,13 @@ JS_KEYWORD(void, TOK_UNARYOP, JSOP_VOID, JSVERSION_DEFAULT)
JS_KEYWORD(while, TOK_WHILE, JSOP_NOP, JSVERSION_DEFAULT)
JS_KEYWORD(with, TOK_WITH, JSOP_NOP, JSVERSION_DEFAULT)
/*
* ES5 FutureReservedWord keywords which we historically unreserved, forcing us
* to re-reserve them only in strict mode.
*/
JS_KEYWORD(class, TOK_STRICT_RESERVED, JSOP_NOP, JSVERSION_DEFAULT)
JS_KEYWORD(enum, TOK_STRICT_RESERVED, JSOP_NOP, JSVERSION_DEFAULT)
JS_KEYWORD(export, TOK_STRICT_RESERVED, JSOP_NOP, JSVERSION_DEFAULT)
JS_KEYWORD(extends, TOK_STRICT_RESERVED, JSOP_NOP, JSVERSION_DEFAULT)
JS_KEYWORD(import, TOK_STRICT_RESERVED, JSOP_NOP, JSVERSION_DEFAULT)
JS_KEYWORD(super, TOK_STRICT_RESERVED, JSOP_NOP, JSVERSION_DEFAULT)
/* ES5 reserved keywords reserved in all code. */
JS_KEYWORD(class, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT)
JS_KEYWORD(enum, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT)
JS_KEYWORD(export, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT)
JS_KEYWORD(extends, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT)
JS_KEYWORD(import, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT)
JS_KEYWORD(super, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT)
/*
* ES5 reserved keywords with long-implemented behavior, allowed in our

Просмотреть файл

@ -144,7 +144,7 @@ extern JSBool js_IsRuntimeLocked(JSRuntime *rt);
#define JS_ATOMIC_ADD(p,v) (*(p) += (v))
#define JS_ATOMIC_SET(p,v) (*(p) = (v))
#define JS_CurrentThreadId() 0
#define js_CurrentThreadId() 0
#define JS_NEW_LOCK() NULL
#define JS_DESTROY_LOCK(l) ((void)0)
#define JS_ACQUIRE_LOCK(l) ((void)0)

Просмотреть файл

@ -944,7 +944,6 @@ static JSFunctionSpec number_methods[] = {
JS_TN(js_toString_str, num_toString, 1, 0, &num_toString_trcinfo),
JS_FN(js_toLocaleString_str, num_toLocaleString, 0, 0),
JS_FN(js_valueOf_str, js_num_valueOf, 0, 0),
JS_FN(js_toJSON_str, js_num_valueOf, 0, 0),
JS_FN("toFixed", num_toFixed, 1, 0),
JS_FN("toExponential", num_toExponential, 1, 0),
JS_FN("toPrecision", num_toPrecision, 1, 0),

Просмотреть файл

@ -1187,7 +1187,7 @@ EvalKernel(JSContext *cx, uintN argc, Value *vp, EvalType evalType, JSStackFrame
/* ES5 15.1.2.1 steps 2-8. */
JSObject *callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, Jsvalify(vp)));
JS_ASSERT(IsBuiltinEvalFunction(callee->getFunctionPrivate()));
JS_ASSERT(IsAnyBuiltinEval(callee->getFunctionPrivate()));
JSPrincipals *principals = js_EvalFramePrincipals(cx, callee, caller);
/*
@ -1306,8 +1306,16 @@ EvalKernel(JSContext *cx, uintN argc, Value *vp, EvalType evalType, JSStackFrame
return ok;
}
JS_FRIEND_API(bool)
IsBuiltinEvalFunction(JSFunction *fun)
bool
IsBuiltinEvalForScope(JSObject *scopeChain, const Value &v)
{
JSObject *global = scopeChain->getGlobal();
JS_ASSERT((global->getClass()->flags & JSCLASS_GLOBAL_FLAGS) == JSCLASS_GLOBAL_FLAGS);
return global->getReservedSlot(JSRESERVED_GLOBAL_EVAL) == v;
}
bool
IsAnyBuiltinEval(JSFunction *fun)
{
return fun->maybeNative() == eval;
}
@ -1320,53 +1328,33 @@ static JSBool
obj_watch_handler(JSContext *cx, JSObject *obj, jsid id, jsval old,
jsval *nvp, void *closure)
{
JSObject *callable;
JSSecurityCallbacks *callbacks;
JSStackFrame *caller;
JSPrincipals *subject, *watcher;
JSResolvingKey key;
JSResolvingEntry *entry;
uint32 generation;
Value argv[3];
JSBool ok;
callable = (JSObject *) closure;
callbacks = JS_GetSecurityCallbacks(cx);
JSObject *callable = (JSObject *) closure;
JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx);
if (callbacks && callbacks->findObjectPrincipals) {
/* Skip over any obj_watch_* frames between us and the real subject. */
caller = js_GetScriptedCaller(cx, NULL);
if (caller) {
if (JSStackFrame *caller = js_GetScriptedCaller(cx, NULL)) {
/*
* Only call the watch handler if the watcher is allowed to watch
* the currently executing script.
*/
watcher = callbacks->findObjectPrincipals(cx, callable);
subject = js_StackFramePrincipals(cx, caller);
JSPrincipals *watcher = callbacks->findObjectPrincipals(cx, callable);
JSPrincipals *subject = js_StackFramePrincipals(cx, caller);
if (watcher && subject && !watcher->subsume(watcher, subject)) {
/* Silently don't call the watch handler. */
return JS_TRUE;
return true;
}
}
}
/* Avoid recursion on (obj, id) already being watched on cx. */
key.obj = obj;
key.id = id;
if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
return JS_FALSE;
if (!entry)
return JS_TRUE;
generation = cx->resolvingTable->generation;
AutoResolving resolving(cx, obj, id, AutoResolving::WATCH);
if (resolving.alreadyStarted())
return true;
argv[0] = IdToValue(id);
argv[1] = Valueify(old);
argv[2] = Valueify(*nvp);
ok = ExternalInvoke(cx, ObjectValue(*obj), ObjectOrNullValue(callable), 3, argv,
Valueify(nvp));
js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
return ok;
Value argv[] = { IdToValue(id), Valueify(old), Valueify(*nvp) };
return ExternalInvoke(cx, ObjectValue(*obj), ObjectOrNullValue(callable),
JS_ARRAY_LENGTH(argv), argv, Valueify(nvp));
}
static JSBool
@ -1879,7 +1867,7 @@ obj_keys(JSContext *cx, uintN argc, Value *vp)
JSString *str = js_IntToString(cx, JSID_TO_INT(id));
if (!str)
return false;
JS_ALWAYS_TRUE(vals.append(StringValue(str)));
vals.infallibleAppend(StringValue(str));
} else {
JS_ASSERT(JSID_IS_OBJECT(id));
}
@ -1915,7 +1903,6 @@ HasProperty(JSContext* cx, JSObject* obj, jsid id, Value* vp, bool *foundp)
PropDesc::PropDesc()
: pd(UndefinedValue()),
id(INT_TO_JSID(0)),
value(UndefinedValue()),
get(UndefinedValue()),
set(UndefinedValue()),
@ -1930,10 +1917,9 @@ PropDesc::PropDesc()
}
bool
PropDesc::initialize(JSContext* cx, jsid id, const Value &origval)
PropDesc::initialize(JSContext* cx, const Value &origval)
{
Value v = origval;
this->id = id;
/* 8.10.5 step 1 */
if (v.isPrimitive()) {
@ -2063,14 +2049,14 @@ Reject(JSContext *cx, JSObject *obj, uintN errorNumber, bool throwError, bool *r
}
static JSBool
DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc,
DefinePropertyOnObject(JSContext *cx, JSObject *obj, const jsid &id, const PropDesc &desc,
bool throwError, bool *rval)
{
/* 8.12.9 step 1. */
JSProperty *current;
JSObject *obj2;
JS_ASSERT(!obj->getOps()->lookupProperty);
if (!js_HasOwnProperty(cx, NULL, obj, desc.id, &obj2, &current))
if (!js_HasOwnProperty(cx, NULL, obj, id, &obj2, &current))
return JS_FALSE;
JS_ASSERT(!obj->getOps()->defineProperty);
@ -2101,7 +2087,7 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc,
if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
JS_ASSERT(!obj->getOps()->defineProperty);
return js_DefineProperty(cx, obj, desc.id, &desc.value,
return js_DefineProperty(cx, obj, id, &desc.value,
PropertyStub, StrictPropertyStub, desc.attrs);
}
@ -2113,11 +2099,11 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc,
*/
Value dummy;
uintN dummyAttrs;
if (!CheckAccess(cx, obj, desc.id, JSACC_WATCH, &dummy, &dummyAttrs))
if (!CheckAccess(cx, obj, id, JSACC_WATCH, &dummy, &dummyAttrs))
return JS_FALSE;
Value tmp = UndefinedValue();
return js_DefineProperty(cx, obj, desc.id, &tmp,
return js_DefineProperty(cx, obj, id, &tmp,
desc.getter(), desc.setter(), desc.attrs);
}
@ -2179,7 +2165,7 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc,
desc.isDataDescriptor() &&
(desc.hasWritable ? desc.writable() : shape->writable()))
{
return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval);
return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
}
if (!js_NativeGet(cx, obj, obj2, shape, JSGET_NO_METHOD_BARRIER, &v))
@ -2214,7 +2200,7 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc,
if (!shape->configurable() &&
(!shape->hasDefaultGetter() || !shape->hasDefaultSetter()))
{
return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval);
return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
}
break;
}
@ -2249,7 +2235,7 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc,
JS_ASSERT_IF(!desc.hasConfigurable, !desc.configurable());
if (desc.configurable() ||
(desc.hasEnumerable && desc.enumerable() != shape->enumerable())) {
return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval);
return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
}
}
@ -2260,19 +2246,19 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc,
} else if (desc.isDataDescriptor() != shape->isDataDescriptor()) {
/* 8.12.9 step 9. */
if (!shape->configurable())
return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval);
return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
} else if (desc.isDataDescriptor()) {
/* 8.12.9 step 10. */
JS_ASSERT(shape->isDataDescriptor());
if (!shape->configurable() && !shape->writable()) {
if (desc.hasWritable && desc.writable())
return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval);
return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
if (desc.hasValue) {
JSBool same;
if (!SameValue(cx, desc.value, v, &same))
return JS_FALSE;
if (!same)
return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval);
return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
}
}
@ -2286,7 +2272,7 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc,
if (!SameValue(cx, desc.setterValue(), shape->setterOrUndefined(), &same))
return JS_FALSE;
if (!same)
return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval);
return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
}
if (desc.hasGet) {
@ -2294,7 +2280,7 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc,
if (!SameValue(cx, desc.getterValue(), shape->getterOrUndefined(), &same))
return JS_FALSE;
if (!same)
return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval);
return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
}
}
}
@ -2341,7 +2327,7 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc,
* control point of view.
*/
Value dummy;
if (!CheckAccess(cx, obj2, desc.id, JSACC_WATCH, &dummy, &attrs))
if (!CheckAccess(cx, obj2, id, JSACC_WATCH, &dummy, &attrs))
return JS_FALSE;
JS_ASSERT_IF(shape->isMethod(), !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
@ -2387,15 +2373,15 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc,
*/
if (callDelProperty) {
Value dummy = UndefinedValue();
if (!CallJSPropertyOp(cx, obj2->getClass()->delProperty, obj2, desc.id, &dummy))
if (!CallJSPropertyOp(cx, obj2->getClass()->delProperty, obj2, id, &dummy))
return false;
}
return js_DefineProperty(cx, obj, desc.id, &v, getter, setter, attrs);
return js_DefineProperty(cx, obj, id, &v, getter, setter, attrs);
}
static JSBool
DefinePropertyOnArray(JSContext *cx, JSObject *obj, const PropDesc &desc,
DefinePropertyOnArray(JSContext *cx, JSObject *obj, const jsid &id, const PropDesc &desc,
bool throwError, bool *rval)
{
/*
@ -2410,7 +2396,7 @@ DefinePropertyOnArray(JSContext *cx, JSObject *obj, const PropDesc &desc,
jsuint oldLen = obj->getArrayLength();
if (JSID_IS_ATOM(desc.id, cx->runtime->atomState.lengthAtom)) {
if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
/*
* Our optimization of storage of the length property of arrays makes
* it very difficult to properly implement defining the property. For
@ -2423,13 +2409,13 @@ DefinePropertyOnArray(JSContext *cx, JSObject *obj, const PropDesc &desc,
}
uint32 index;
if (js_IdIsIndex(desc.id, &index)) {
if (js_IdIsIndex(id, &index)) {
/*
// Disabled until we support defining "length":
if (index >= oldLen && lengthPropertyNotWritable())
return ThrowTypeError(cx, JSMSG_CANT_APPEND_TO_ARRAY);
*/
if (!DefinePropertyOnObject(cx, obj, desc, false, rval))
if (!DefinePropertyOnObject(cx, obj, id, desc, false, rval))
return JS_FALSE;
if (!*rval)
return Reject(cx, obj, JSMSG_CANT_DEFINE_ARRAY_INDEX, throwError, rval);
@ -2443,36 +2429,35 @@ DefinePropertyOnArray(JSContext *cx, JSObject *obj, const PropDesc &desc,
return JS_TRUE;
}
return DefinePropertyOnObject(cx, obj, desc, throwError, rval);
return DefinePropertyOnObject(cx, obj, id, desc, throwError, rval);
}
static JSBool
DefineProperty(JSContext *cx, JSObject *obj, const PropDesc &desc, bool throwError,
DefineProperty(JSContext *cx, JSObject *obj, const jsid &id, const PropDesc &desc, bool throwError,
bool *rval)
{
if (obj->isArray())
return DefinePropertyOnArray(cx, obj, desc, throwError, rval);
return DefinePropertyOnArray(cx, obj, id, desc, throwError, rval);
if (obj->getOps()->lookupProperty) {
if (obj->isProxy())
return JSProxy::defineProperty(cx, obj, desc.id, desc.pd);
return JSProxy::defineProperty(cx, obj, id, desc.pd);
return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
}
return DefinePropertyOnObject(cx, obj, desc, throwError, rval);
return DefinePropertyOnObject(cx, obj, id, desc, throwError, rval);
}
JSBool
js_DefineOwnProperty(JSContext *cx, JSObject *obj, jsid id,
const Value &descriptor, JSBool *bp)
js_DefineOwnProperty(JSContext *cx, JSObject *obj, jsid id, const Value &descriptor, JSBool *bp)
{
AutoPropDescArrayRooter descs(cx);
PropDesc *desc = descs.append();
if (!desc || !desc->initialize(cx, id, descriptor))
if (!desc || !desc->initialize(cx, descriptor))
return false;
bool rval;
if (!DefineProperty(cx, obj, *desc, true, &rval))
if (!DefineProperty(cx, obj, id, *desc, true, &rval))
return false;
*bp = !!rval;
return true;
@ -2504,26 +2489,23 @@ obj_defineProperty(JSContext* cx, uintN argc, Value* vp)
static bool
DefineProperties(JSContext *cx, JSObject *obj, JSObject *props)
{
AutoIdArray ida(cx, JS_Enumerate(cx, props));
if (!ida)
AutoIdVector ids(cx);
if (!GetPropertyNames(cx, props, JSITER_OWNONLY, &ids))
return false;
AutoPropDescArrayRooter descs(cx);
size_t len = ida.length();
size_t len = ids.length();
for (size_t i = 0; i < len; i++) {
jsid id = ida[i];
jsid id = ids[i];
PropDesc* desc = descs.append();
AutoValueRooter tvr(cx);
if (!desc ||
!JS_GetPropertyById(cx, props, id, tvr.jsval_addr()) ||
!desc->initialize(cx, id, tvr.value())) {
Value v;
if (!desc || !props->getProperty(cx, id, &v) || !desc->initialize(cx, v))
return false;
}
}
bool dummy;
for (size_t i = 0; i < len; i++) {
if (!DefineProperty(cx, obj, descs[i], true, &dummy))
if (!DefineProperty(cx, obj, ids[i], descs[i], true, &dummy))
return false;
}
@ -2540,23 +2522,23 @@ js_PopulateObject(JSContext *cx, JSObject *newborn, JSObject *props)
static JSBool
obj_defineProperties(JSContext* cx, uintN argc, Value* vp)
{
/* 15.2.3.6 steps 1 and 5. */
/* Steps 1 and 7. */
JSObject *obj;
if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.defineProperties", &obj))
return false;
vp->setObject(*obj);
/* Step 2. */
if (argc < 2) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
"Object.defineProperties", "0", "s");
return false;
}
JSObject* props = js_ValueToNonNullObject(cx, vp[3]);
JSObject* props = ToObject(cx, &vp[3]);
if (!props)
return false;
vp[3].setObject(*props);
/* Steps 3-6. */
return DefineProperties(cx, obj, props);
}
@ -2598,27 +2580,8 @@ obj_create(JSContext *cx, uintN argc, Value *vp)
return JS_FALSE;
}
JSObject *props = &vp[3].toObject();
AutoIdArray ida(cx, JS_Enumerate(cx, props));
if (!ida)
if (!DefineProperties(cx, obj, &vp[3].toObject()))
return JS_FALSE;
AutoPropDescArrayRooter descs(cx);
size_t len = ida.length();
for (size_t i = 0; i < len; i++) {
jsid id = ida[i];
PropDesc *desc = descs.append();
if (!desc || !JS_GetPropertyById(cx, props, id, Jsvalify(&vp[1])) ||
!desc->initialize(cx, id, vp[1])) {
return JS_FALSE;
}
}
bool dummy;
for (size_t i = 0; i < len; i++) {
if (!DefineProperty(cx, obj, descs[i], true, &dummy))
return JS_FALSE;
}
}
/* 5. Return obj. */
@ -3221,7 +3184,7 @@ Class js_WithClass = {
NULL, /* construct */
NULL, /* xdrObject */
NULL, /* hasInstance */
NULL, /* mark */
NULL, /* trace */
JS_NULL_CLASS_EXT,
{
with_LookupProperty,
@ -3233,7 +3196,6 @@ Class js_WithClass = {
with_DeleteProperty,
with_Enumerate,
with_TypeOf,
NULL, /* trace */
NULL, /* fix */
with_ThisObject,
NULL, /* clear */
@ -3251,8 +3213,12 @@ js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
JSStackFrame *priv = js_FloatingFrameIfGenerator(cx, cx->fp());
EmptyShape *emptyWithShape = EmptyShape::getEmptyWithShape(cx);
if (!emptyWithShape)
return NULL;
obj->init(cx, &js_WithClass, proto, parent, priv, false);
obj->setMap(cx->compartment->emptyWithShape);
obj->setMap(emptyWithShape);
OBJ_SET_BLOCK_DEPTH(cx, obj, depth);
AutoObjectRooter tvr(cx, obj);
@ -3277,8 +3243,12 @@ js_NewBlockObject(JSContext *cx)
if (!blockObj)
return NULL;
EmptyShape *emptyBlockShape = EmptyShape::getEmptyBlockShape(cx);
if (!emptyBlockShape)
return NULL;
blockObj->init(cx, &js_BlockClass, NULL, NULL, NULL, false);
blockObj->setMap(cx->compartment->emptyBlockShape);
blockObj->setMap(emptyBlockShape);
return blockObj;
}
@ -3297,9 +3267,8 @@ js_CloneBlockObject(JSContext *cx, JSObject *proto, JSStackFrame *fp)
JSStackFrame *priv = js_FloatingFrameIfGenerator(cx, fp);
/* The caller sets parent on its own. */
clone->init(cx, &js_BlockClass, proto, NULL, priv, false);
clone->initClonedBlock(cx, proto, priv);
clone->setMap(proto->map);
if (!clone->ensureInstanceReservedSlots(cx, count + 1))
return NULL;
@ -3713,13 +3682,12 @@ js_XDRBlockObject(JSXDRState *xdr, JSObject **objp)
*/
for (uintN i = 0; i < count; i++) {
JSAtom *atom;
uint16 shortid;
/* XDR the real id, then the shortid. */
if (!js_XDRAtom(xdr, &atom) || !JS_XDRUint16(xdr, &shortid))
/* XDR the real id. */
if (!js_XDRAtom(xdr, &atom))
return false;
if (!obj->defineBlockVariable(cx, ATOM_TO_JSID(atom), shortid))
if (!obj->defineBlockVariable(cx, ATOM_TO_JSID(atom), i))
return false;
}
} else {
@ -3743,11 +3711,13 @@ js_XDRBlockObject(JSXDRState *xdr, JSObject **objp)
JS_ASSERT(JSID_IS_ATOM(propid));
JSAtom *atom = JSID_TO_ATOM(propid);
#ifdef DEBUG
uint16 shortid = uint16(shape->shortid);
JS_ASSERT(shortid == i);
#endif
/* XDR the real id, then the shortid. */
if (!js_XDRAtom(xdr, &atom) || !JS_XDRUint16(xdr, &shortid))
/* XDR the real id. */
if (!js_XDRAtom(xdr, &atom))
return false;
}
}
@ -3778,8 +3748,13 @@ js_InitObjectClass(JSContext *cx, JSObject *obj)
/* ECMA (15.1.2.1) says 'eval' is a property of the global object. */
jsid id = ATOM_TO_JSID(cx->runtime->atomState.evalAtom);
if (!js_DefineFunction(cx, obj, id, eval, 1, JSFUN_STUB_GSOPS))
JSObject *evalobj = js_DefineFunction(cx, obj, id, eval, 1, JSFUN_STUB_GSOPS);
if (!evalobj)
return NULL;
if (obj->isGlobal()) {
if (!js_SetReservedSlot(cx, obj, JSRESERVED_GLOBAL_EVAL, ObjectValue(*evalobj)))
return NULL;
}
return proto;
}
@ -4179,52 +4154,36 @@ JSBool
js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
JSObject **objp)
{
JSObject *cobj;
JSResolvingKey rkey;
JSResolvingEntry *rentry;
uint32 generation;
JSObjectOp init;
Value v;
obj = obj->getGlobal();
if (!obj->isGlobal()) {
*objp = NULL;
return JS_TRUE;
return true;
}
v = obj->getReservedSlot(key);
Value v = obj->getReservedSlot(key);
if (v.isObject()) {
*objp = &v.toObject();
return JS_TRUE;
return true;
}
rkey.obj = obj;
rkey.id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]);
if (!js_StartResolving(cx, &rkey, JSRESFLAG_LOOKUP, &rentry))
return JS_FALSE;
if (!rentry) {
/* Already caching key in obj -- suppress recursion. */
AutoResolving resolving(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]));
if (resolving.alreadyStarted()) {
/* Already caching id in obj -- suppress recursion. */
*objp = NULL;
return JS_TRUE;
}
generation = cx->resolvingTable->generation;
JSBool ok = true;
cobj = NULL;
init = lazy_prototype_init[key];
if (init) {
if (!init(cx, obj)) {
ok = JS_FALSE;
} else {
v = obj->getReservedSlot(key);
if (v.isObject())
cobj = &v.toObject();
}
return true;
}
JSObject *cobj = NULL;
if (JSObjectOp init = lazy_prototype_init[key]) {
if (!init(cx, obj))
return false;
v = obj->getReservedSlot(key);
if (v.isObject())
cobj = &v.toObject();
}
js_StopResolving(cx, &rkey, JSRESFLAG_LOOKUP, rentry, generation);
*objp = cobj;
return ok;
return true;
}
JSBool
@ -4834,8 +4793,7 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &valu
JS_SCOPE_DEPTH_METERING(JS_BASIC_STATS_ACCUM(bs, val))
/*
* Call obj's resolve hook. obj is a native object and the caller holds its
* scope lock.
* Call obj's resolve hook.
*
* cx, start, id, and flags are the parameters initially passed to the ongoing
* lookup; objp and propp are its out parameters. obj is an object along
@ -4843,14 +4801,13 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &valu
*
* There are four possible outcomes:
*
* - On failure, report an error or exception, unlock obj, and return false.
* - On failure, report an error or exception and return false.
*
* - If we are alrady resolving a property of *curobjp, set *recursedp = true,
* unlock obj, and return true.
* - If we are already resolving a property of *curobjp, set *recursedp = true,
* and return true.
*
* - If the resolve hook finds or defines the sought property, set *objp and
* *propp appropriately, set *recursedp = false, and return true with *objp's
* lock held.
* *propp appropriately, set *recursedp = false, and return true.
*
* - Otherwise no property was resolved. Set *propp = NULL and *recursedp = false
* and return true.
@ -4870,80 +4827,52 @@ CallResolveOp(JSContext *cx, JSObject *start, JSObject *obj, jsid id, uintN flag
* returning. But note that JS_DHASH_ADD may find an existing
* entry, in which case we bail to suppress runaway recursion.
*/
JSResolvingKey key = {obj, id};
JSResolvingEntry *entry;
if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry))
return false;
if (!entry) {
AutoResolving resolving(cx, obj, id);
if (resolving.alreadyStarted()) {
/* Already resolving id in obj -- suppress recursion. */
*recursedp = true;
return true;
}
uint32 generation = cx->resolvingTable->generation;
*recursedp = false;
*propp = NULL;
JSBool ok;
const Shape *shape = NULL;
if (clasp->flags & JSCLASS_NEW_RESOLVE) {
JSNewResolveOp newresolve = (JSNewResolveOp)resolve;
JSNewResolveOp newresolve = reinterpret_cast<JSNewResolveOp>(resolve);
if (flags == JSRESOLVE_INFER)
flags = js_InferFlags(cx, 0);
JSObject *obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START) ? start : NULL;
if (!newresolve(cx, obj, id, flags, &obj2))
return false;
{
/* Protect id and all atoms from a GC nested in resolve. */
AutoKeepAtoms keep(cx->runtime);
ok = newresolve(cx, obj, id, flags, &obj2);
}
if (!ok)
goto cleanup;
if (obj2) {
/* Resolved: lookup id again for backward compatibility. */
if (!obj2->isNative()) {
/* Whoops, newresolve handed back a foreign obj2. */
JS_ASSERT(obj2 != obj);
ok = obj2->lookupProperty(cx, id, objp, propp);
if (!ok || *propp)
goto cleanup;
} else {
/*
* Require that obj2 not be empty now, as we do for old-style
* resolve. If it doesn't, then id was not truly resolved, and
* we'll find it in the proto chain, or miss it if obj2's proto
* is not on obj's proto chain. That last case is a "too bad!"
* case.
*/
if (!obj2->nativeEmpty())
shape = obj2->nativeLookup(id);
}
if (shape) {
JS_ASSERT(!obj2->nativeEmpty());
obj = obj2;
}
}
} else {
/*
* Old resolve always requires id re-lookup if obj is not empty after
* resolve returns.
* We trust the new style resolve hook to set obj2 to NULL when
* the id cannot be resolved. But, when obj2 is not null, we do
* not assume that id must exist and do full nativeLookup for
* compatibility.
*/
ok = resolve(cx, obj, id);
if (!ok)
goto cleanup;
JS_ASSERT(obj->isNative());
if (!obj->nativeEmpty())
shape = obj->nativeLookup(id);
if (!obj2)
return true;
if (!obj2->isNative()) {
/* Whoops, newresolve handed back a foreign obj2. */
JS_ASSERT(obj2 != obj);
return obj2->lookupProperty(cx, id, objp, propp);
}
obj = obj2;
} else {
if (!resolve(cx, obj, id))
return false;
}
cleanup:
if (ok && shape) {
*objp = obj;
*propp = (JSProperty *) shape;
if (!obj->nativeEmpty()) {
if (const Shape *shape = obj->nativeLookup(id)) {
*objp = obj;
*propp = (JSProperty *) shape;
}
}
js_StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation);
return ok;
return true;
}
static JS_ALWAYS_INLINE int
@ -5586,8 +5515,10 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
if (!JSProxy::getPropertyDescriptor(cx, pobj, id, true, &pd))
return false;
if (pd.attrs & JSPROP_SHARED)
return CallSetter(cx, obj, id, pd.setter, pd.attrs, pd.shortid, strict, vp);
if ((pd.attrs & (JSPROP_SHARED | JSPROP_SHADOWABLE)) == JSPROP_SHARED) {
return !pd.setter ||
CallSetter(cx, obj, id, pd.setter, pd.attrs, pd.shortid, strict, vp);
}
if (pd.attrs & JSPROP_READONLY) {
if (strict)
@ -6130,18 +6061,7 @@ CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
JSType
js_TypeOf(JSContext *cx, JSObject *obj)
{
/*
* ECMA 262, 11.4.3 says that any native object that implements
* [[Call]] should be of type "function". However, RegExp is of
* type "object", not "function", for Web compatibility.
*/
if (obj->isCallable()) {
return (obj->getClass() != &js_RegExpClass)
? JSTYPE_FUNCTION
: JSTYPE_OBJECT;
}
return JSTYPE_OBJECT;
return obj->isCallable() ? JSTYPE_FUNCTION : JSTYPE_OBJECT;
}
bool
@ -6443,12 +6363,16 @@ js_XDRObject(JSXDRState *xdr, JSObject **objp)
JSBasicStats js_entry_count_bs = JS_INIT_STATIC_BASIC_STATS;
static void
namespace js {
void
MeterEntryCount(uintN count)
{
JS_BASIC_STATS_ACCUM(&js_entry_count_bs, count);
}
}
void
js_DumpScopeMeters(JSRuntime *rt)
{
@ -6518,58 +6442,6 @@ js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize)
}
#endif
void
js_TraceObject(JSTracer *trc, JSObject *obj)
{
JS_ASSERT(obj->isNative());
JSContext *cx = trc->context;
if (obj->hasSlotsArray() && !obj->nativeEmpty() && IS_GC_MARKING_TRACER(trc)) {
/*
* Trim overlong dslots allocations from the GC, to avoid thrashing in
* case of delete-happy code that settles down at a given population.
* The !obj->nativeEmpty() guard above is due to the bug described by
* the FIXME comment below.
*/
size_t slots = obj->slotSpan();
if (obj->numSlots() != slots)
obj->shrinkSlots(cx, slots);
}
#ifdef JS_DUMP_SCOPE_METERS
MeterEntryCount(obj->propertyCount);
#endif
obj->trace(trc);
if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList))
js_TraceWatchPoints(trc, obj);
/* No one runs while the GC is running, so we can use LOCKED_... here. */
Class *clasp = obj->getClass();
if (clasp->mark) {
if (clasp->flags & JSCLASS_MARK_IS_TRACE)
((JSTraceOp) clasp->mark)(trc, obj);
else if (IS_GC_MARKING_TRACER(trc))
(void) clasp->mark(cx, obj, trc);
}
if (clasp->flags & JSCLASS_IS_GLOBAL) {
JSCompartment *compartment = obj->getCompartment();
compartment->mark(trc);
}
/*
* NB: clasp->mark could mutate something (which would be a bug, but we are
* defensive), so don't hoist this above calling clasp->mark.
*/
uint32 nslots = Min(obj->numSlots(), obj->slotSpan());
for (uint32 i = 0; i != nslots; ++i) {
const Value &v = obj->getSlot(i);
JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i);
MarkValueRaw(trc, v);
}
}
void
js_ClearNative(JSContext *cx, JSObject *obj)
{

Просмотреть файл

@ -122,20 +122,37 @@ CastAsObjectJsval(StrictPropertyOp op)
return ObjectOrNullValue(CastAsObject(op));
}
} /* namespace js */
/*
* A representation of ECMA-262 ed. 5's internal property descriptor data
* A representation of ECMA-262 ed. 5's internal Property Descriptor data
* structure.
*/
struct PropDesc {
/*
* Original object from which this descriptor derives, passed through for
* the benefit of proxies.
*/
js::Value pd;
js::Value value, get, set;
/* Property descriptor boolean fields. */
uint8 attrs;
/* Bits indicating which values are set. */
bool hasGet : 1;
bool hasSet : 1;
bool hasValue : 1;
bool hasWritable : 1;
bool hasEnumerable : 1;
bool hasConfigurable : 1;
friend class js::AutoPropDescArrayRooter;
PropDesc();
public:
/* 8.10.5 ToPropertyDescriptor(Obj) */
bool initialize(JSContext* cx, jsid id, const js::Value &v);
bool initialize(JSContext* cx, const js::Value &v);
/* 8.10.1 IsAccessorDescriptor(desc) */
bool isAccessorDescriptor() const {
@ -184,27 +201,13 @@ struct PropDesc {
js::StrictPropertyOp setter() const {
return js::CastAsStrictPropertyOp(setterObject());
}
js::Value pd;
jsid id;
js::Value value, get, set;
/* Property descriptor boolean fields. */
uint8 attrs;
/* Bits indicating which values are set. */
bool hasGet : 1;
bool hasSet : 1;
bool hasValue : 1;
bool hasWritable : 1;
bool hasEnumerable : 1;
bool hasConfigurable : 1;
};
namespace js {
typedef Vector<PropDesc, 1> PropDescArray;
void
MeterEntryCount(uintN count);
} /* namespace js */
struct JSObjectMap {
@ -502,16 +505,21 @@ struct JSObject : js::gc::Cell {
bool hasOwnShape() const { return !!(flags & OWN_SHAPE); }
void setMap(const JSObjectMap *amap) {
void setMap(JSObjectMap *amap) {
JS_ASSERT(!hasOwnShape());
map = const_cast<JSObjectMap *>(amap);
map = amap;
objShape = map->shape;
}
void setSharedNonNativeMap() {
setMap(&JSObjectMap::sharedNonNative);
setMap(const_cast<JSObjectMap *>(&JSObjectMap::sharedNonNative));
}
/* Functions for setting up scope chain object maps and shapes. */
void initCall(JSContext *cx, const js::Bindings *bindings, JSObject *parent);
void initClonedBlock(JSContext *cx, JSObject *proto, JSStackFrame *priv);
void setBlockOwnShape(JSContext *cx);
void deletingShapeChange(JSContext *cx, const js::Shape &shape);
const js::Shape *methodShapeChange(JSContext *cx, const js::Shape &shape);
bool methodShapeChange(JSContext *cx, uint32 slot);
@ -1053,6 +1061,12 @@ struct JSObject : js::gc::Cell {
inline js::NativeIterator *getNativeIterator() const;
inline void setNativeIterator(js::NativeIterator *);
/*
* Script-related getters.
*/
inline JSScript *getScript() const;
/*
* XML-related getters and setters.
*/
@ -1296,6 +1310,7 @@ struct JSObject : js::gc::Cell {
inline bool isClonedBlock() const;
inline bool isCall() const;
inline bool isRegExp() const;
inline bool isScript() const;
inline bool isXML() const;
inline bool isXMLId() const;
inline bool isNamespace() const;
@ -1660,6 +1675,11 @@ extern int
js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
JSObject **objp, JSProperty **propp);
/*
* Constant to pass to js_LookupPropertyWithFlags to infer bits from current
* bytecode.
*/
static const uintN JSRESOLVE_INFER = 0xffff;
extern JS_FRIEND_DATA(js::Class) js_CallClass;
extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass;
@ -1857,9 +1877,6 @@ js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom,
extern JSBool
js_XDRObject(JSXDRState *xdr, JSObject **objp);
extern void
js_TraceObject(JSTracer *trc, JSObject *obj);
extern void
js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize);
@ -1933,8 +1950,16 @@ extern bool
EvalKernel(JSContext *cx, uintN argc, js::Value *vp, EvalType evalType, JSStackFrame *caller,
JSObject *scopeobj);
extern JS_FRIEND_API(bool)
IsBuiltinEvalFunction(JSFunction *fun);
/*
* 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);
}

Просмотреть файл

@ -59,6 +59,7 @@
#include "jscntxt.h"
#include "jsnum.h"
#include "jsscopeinlines.h"
#include "jsscriptinlines.h"
#include "jsstr.h"
#include "jsfuninlines.h"
@ -141,6 +142,59 @@ JSObject::finalize(JSContext *cx)
finish(cx);
}
/*
* Initializer for Call objects for functions and eval frames. Set class,
* parent, map, and shape, and allocate slots.
*/
inline void
JSObject::initCall(JSContext *cx, const js::Bindings *bindings, JSObject *parent)
{
init(cx, &js_CallClass, NULL, parent, NULL, false);
map = bindings->lastShape();
/*
* If |bindings| is for a function that has extensible parents, that means
* its Call should have its own shape; see js::Bindings::extensibleParents.
*/
if (bindings->extensibleParents())
setOwnShape(js_GenerateShape(cx));
else
objShape = map->shape;
}
/*
* Initializer for cloned block objects. Set class, prototype, frame, map, and
* shape.
*/
inline void
JSObject::initClonedBlock(JSContext *cx, JSObject *proto, JSStackFrame *frame)
{
init(cx, &js_BlockClass, proto, NULL, frame, false);
/* Cloned blocks copy their prototype's map; it had better be shareable. */
JS_ASSERT(!proto->inDictionaryMode() || proto->lastProp->frozen());
map = proto->map;
/*
* If the prototype has its own shape, that means the clone should, too; see
* js::Bindings::extensibleParents.
*/
if (proto->hasOwnShape())
setOwnShape(js_GenerateShape(cx));
else
objShape = map->shape;
}
/*
* Mark a compile-time block as OWN_SHAPE, indicating that its run-time clones
* also need unique shapes. See js::Bindings::extensibleParents.
*/
inline void
JSObject::setBlockOwnShape(JSContext *cx) {
JS_ASSERT(isStaticBlock());
setOwnShape(js_GenerateShape(cx));
}
/*
* Property read barrier for deferred cloning of compiler-created function
* objects optimized as typically non-escaping, ad-hoc methods in obj.

Просмотреть файл

@ -494,37 +494,60 @@ CallReplacerFunction(JSContext *cx, jsid id, JSObject *holder, StringifyContext
static JSBool
Str(JSContext *cx, jsid id, JSObject *holder, StringifyContext *scx, Value *vp, bool callReplacer)
{
JS_CHECK_RECURSION(cx, return JS_FALSE);
JS_CHECK_RECURSION(cx, return false);
/*
* This method implements the Str algorithm in ES5 15.12.3, but we move
* property retrieval (step 1) into callers to stream the stringification
* process and avoid constantly copying strings.
*/
/* Step 2. */
if (vp->isObject() && !js_TryJSON(cx, vp))
return JS_FALSE;
return false;
/* Step 3. */
if (callReplacer && !CallReplacerFunction(cx, id, holder, scx, vp))
return JS_FALSE;
return false;
// catches string and number objects with no toJSON
/* Step 4. */
if (vp->isObject()) {
JSObject *obj = &vp->toObject();
Class *clasp = obj->getClass();
if (clasp == &js_StringClass || clasp == &js_NumberClass)
if (clasp == &js_NumberClass) {
double d;
if (!ValueToNumber(cx, *vp, &d))
return false;
vp->setNumber(d);
} else if (clasp == &js_StringClass) {
JSString *str = js_ValueToString(cx, *vp);
if (!str)
return false;
vp->setString(str);
} else if (clasp == &js_BooleanClass) {
*vp = obj->getPrimitiveThis();
}
}
/* Step 8. */
if (vp->isString()) {
JSString *str = vp->toString();
size_t length = str->length();
const jschar *chars = str->getChars(cx);
if (!chars)
return JS_FALSE;
return false;
return write_string(cx, scx->sb, chars, length);
}
/* Step 5. */
if (vp->isNull())
return scx->sb.append("null");
/* Steps 6-7. */
if (vp->isBoolean())
return vp->toBoolean() ? scx->sb.append("true") : scx->sb.append("false");
/* Step 9. */
if (vp->isNumber()) {
if (vp->isDouble()) {
jsdouble d = vp->toDouble();
@ -534,11 +557,12 @@ Str(JSContext *cx, jsid id, JSObject *holder, StringifyContext *scx, Value *vp,
StringBuffer sb(cx);
if (!NumberValueToStringBuffer(cx, *vp, sb))
return JS_FALSE;
return false;
return scx->sb.append(sb.begin(), sb.length());
}
/* Step 10. */
if (vp->isObject() && !IsFunctionObject(*vp) && !IsXML(*vp)) {
JSBool ok;
@ -549,8 +573,9 @@ Str(JSContext *cx, jsid id, JSObject *holder, StringifyContext *scx, Value *vp,
return ok;
}
/* Step 11. */
vp->setUndefined();
return JS_TRUE;
return true;
}
JSBool

Просмотреть файл

@ -58,7 +58,6 @@
#include "jsatom.h"
#include "jscntxt.h"
#include "jsversion.h"
#include "jsdbgapi.h"
#include "jsemit.h"
#include "jsfun.h"
#include "jsiter.h"
@ -71,7 +70,6 @@
#include "jsscript.h"
#include "jsstr.h"
#include "jsstaticcheck.h"
#include "jstracer.h"
#include "jsvector.h"
#include "jsinterpinlines.h"
@ -5542,8 +5540,7 @@ ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *target,
}
LOCAL_ASSERT(pc == target);
return pcdepth;
#undef LOCAL_ASSERT
}
#undef LOCAL_ASSERT
#undef LOCAL_ASSERT_RV

Просмотреть файл

@ -230,7 +230,7 @@ OPDEF(JSOP_OBJECT, 80, "object", NULL, 3, 0, 1, 19, JOF_OBJECT
/* Pop value and discard it. */
OPDEF(JSOP_POP, 81, "pop", NULL, 1, 1, 0, 2, JOF_BYTE)
/* Convert value to number, for unary +. */
/* Call a function as a constructor; operand is argc. */
OPDEF(JSOP_NEW, 82, js_new_str, NULL, 3, -1, 1, 17, JOF_UINT16|JOF_INVOKE)
/* Trap into debugger for breakpoint, etc. */
@ -311,7 +311,7 @@ OPDEF(JSOP_LINENO, 119,"lineno", NULL, 3, 0, 0, 0, JOF_UINT16
* lval if false; and DEFAULT is POP lval and GOTO.
*/
OPDEF(JSOP_CONDSWITCH,120,"condswitch", NULL, 1, 0, 0, 0, JOF_BYTE|JOF_PARENHEAD)
OPDEF(JSOP_CASE, 121,"case", NULL, 3, 2, 1, 0, JOF_JUMP)
OPDEF(JSOP_CASE, 121,"case", NULL, 3, 2, 1, 0, JOF_JUMP|JOF_TMPSLOT2)
OPDEF(JSOP_DEFAULT, 122,"default", NULL, 3, 1, 0, 0, JOF_JUMP)
/*

Просмотреть файл

@ -195,6 +195,7 @@ Parser::Parser(JSContext *cx, JSPrincipals *prin, JSStackFrame *cfp)
functionCount(0),
traceListHead(NULL),
tc(NULL),
emptyCallShape(NULL),
keepAtoms(cx->runtime)
{
js::PodArrayZero(tempFreeList);
@ -207,6 +208,9 @@ Parser::init(const jschar *base, size_t length, const char *filename, uintN line
JSVersion version)
{
JSContext *cx = context;
emptyCallShape = EmptyShape::getEmptyCallShape(cx);
if (!emptyCallShape)
return false;
tempPoolMark = JS_ARENA_MARK(&cx->tempPool);
if (!tokenStream.init(base, length, filename, lineno, version)) {
JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
@ -289,7 +293,7 @@ Parser::newFunctionBox(JSObject *obj, JSParseNode *fn, JSTreeContext *tc)
funbox->kids = NULL;
funbox->parent = tc->funbox;
funbox->methods = NULL;
new (&funbox->bindings) Bindings(context);
new (&funbox->bindings) Bindings(context, emptyCallShape);
funbox->queued = false;
funbox->inLoop = false;
for (JSStmtInfo *stmt = tc->topStmt; stmt; stmt = stmt->down) {
@ -308,7 +312,7 @@ Parser::newFunctionBox(JSObject *obj, JSParseNode *fn, JSTreeContext *tc)
bool
JSFunctionBox::joinable() const
{
return FUN_NULL_CLOSURE((JSFunction *) object) &&
return FUN_NULL_CLOSURE(function()) &&
!(tcflags & (TCF_FUN_USES_ARGUMENTS | TCF_FUN_USES_OWN_NAME));
}
@ -322,6 +326,12 @@ JSFunctionBox::inAnyDynamicScope() const
return false;
}
bool
JSFunctionBox::scopeIsExtensible() const
{
return tcflags & TCF_FUN_EXTENSIBLE_SCOPE;
}
bool
JSFunctionBox::shouldUnbrand(uintN methods, uintN slowMethods) const
{
@ -347,6 +357,9 @@ Parser::trace(JSTracer *trc)
objbox = objbox->traceLink;
}
if (emptyCallShape)
emptyCallShape->trace(trc);
for (JSTreeContext *tc = this->tc; tc; tc = tc->parent)
tc->trace(trc);
}
@ -1175,7 +1188,7 @@ Compiler::defineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *scrip
Value rval;
if (def.funbox) {
JSFunction *fun = (JSFunction *)def.funbox->object;
JSFunction *fun = def.funbox->function();
/*
* No need to check for redeclarations or anything, global
@ -1373,7 +1386,7 @@ HasFinalReturn(JSParseNode *pn)
}
static JSBool
ReportBadReturn(JSContext *cx, JSTreeContext *tc, uintN flags, uintN errnum,
ReportBadReturn(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, uintN flags, uintN errnum,
uintN anonerrnum)
{
JSAutoByteString name;
@ -1383,7 +1396,7 @@ ReportBadReturn(JSContext *cx, JSTreeContext *tc, uintN flags, uintN errnum,
} else {
errnum = anonerrnum;
}
return ReportCompileErrorNumber(cx, TS(tc->parser), NULL, flags, errnum, name.ptr());
return ReportCompileErrorNumber(cx, TS(tc->parser), pn, flags, errnum, name.ptr());
}
static JSBool
@ -1391,7 +1404,7 @@ CheckFinalReturn(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
{
JS_ASSERT(tc->inFunction());
return HasFinalReturn(pn) == ENDS_IN_RETURN ||
ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT,
ReportBadReturn(cx, tc, pn, JSREPORT_WARNING | JSREPORT_STRICT,
JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
}
@ -1544,7 +1557,7 @@ Parser::functionBody()
pn = NULL;
} else {
if (tc->flags & TCF_FUN_IS_GENERATOR) {
ReportBadReturn(context, tc, JSREPORT_ERROR,
ReportBadReturn(context, tc, pn, JSREPORT_ERROR,
JSMSG_BAD_GENERATOR_RETURN,
JSMSG_BAD_ANON_GENERATOR_RETURN);
pn = NULL;
@ -1821,14 +1834,14 @@ Compiler::compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *prin
JSCodeGenerator funcg(&parser, &codePool, &notePool, tokenStream.getLineno());
if (!funcg.init())
return NULL;
return false;
funcg.flags |= TCF_IN_FUNCTION;
funcg.setFunction(fun);
funcg.bindings.transfer(cx, bindings);
fun->setArgCount(funcg.bindings.countArgs());
if (!GenerateBlockId(&funcg, funcg.bodyid))
return NULL;
return false;
/* FIXME: make Function format the source for a function definition. */
tokenStream.mungeCurrentToken(TOK_NAME);
@ -2040,6 +2053,7 @@ Parser::analyzeFunctions(JSTreeContext *tc)
return true;
if (!markFunArgs(tc->functionList))
return false;
markExtensibleScopeDescendants(tc->functionList, false);
setFunctionKinds(tc->functionList, &tc->flags);
return true;
}
@ -2082,7 +2096,7 @@ FindFunArgs(JSFunctionBox *funbox, int level, JSFunctionBoxQueue *queue)
do {
JSParseNode *fn = funbox->node;
JS_ASSERT(fn->pn_arity == PN_FUNC);
JSFunction *fun = (JSFunction *) funbox->object;
JSFunction *fun = funbox->function();
int fnlevel = level;
/*
@ -2348,7 +2362,7 @@ CanFlattenUpvar(JSDefinition *dn, JSFunctionBox *funbox, uint32 tcflags)
* function refers to its own name) or strictly after afunbox, we also
* defeat the flat closure optimization for this dn.
*/
JSFunction *afun = (JSFunction *) afunbox->object;
JSFunction *afun = afunbox->function();
if (!(afun->flags & JSFUN_LAMBDA)) {
if (dn->isBindingForm() || dn->pn_pos >= afunbox->node->pn_pos)
return false;
@ -2494,7 +2508,7 @@ Parser::setFunctionKinds(JSFunctionBox *funbox, uint32 *tcflags)
}
}
JSFunction *fun = (JSFunction *) funbox->object;
JSFunction *fun = funbox->function();
JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
@ -2653,6 +2667,38 @@ Parser::setFunctionKinds(JSFunctionBox *funbox, uint32 *tcflags)
#undef FUN_METER
}
/*
* Walk the JSFunctionBox tree looking for functions whose call objects may
* acquire new bindings as they execute: non-strict functions that call eval,
* and functions that contain function statements (definitions not appearing
* within the top statement list, which don't take effect unless they are
* evaluated). Such call objects may acquire bindings that shadow variables
* defined in enclosing scopes, so any enclosed functions must have their
* bindings' extensibleParents flags set, and enclosed compiler-created blocks
* must have their OWN_SHAPE flags set; the comments for
* js::Bindings::extensibleParents explain why.
*/
void
Parser::markExtensibleScopeDescendants(JSFunctionBox *funbox, bool hasExtensibleParent)
{
for (; funbox; funbox = funbox->siblings) {
/*
* It would be nice to use FUN_KIND(fun) here to recognize functions
* that will never consult their parent chains, and thus don't need
* their 'extensible parents' flag set. Filed as bug 619750.
*/
JS_ASSERT(!funbox->bindings.extensibleParents());
if (hasExtensibleParent)
funbox->bindings.setExtensibleParents();
if (funbox->kids) {
markExtensibleScopeDescendants(funbox->kids,
hasExtensibleParent || funbox->scopeIsExtensible());
}
}
}
const char js_argument_str[] = "argument";
const char js_variable_str[] = "variable";
const char js_unknown_str[] = "unknown";
@ -3099,6 +3145,13 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
fn->pn_type = TOK_FUNCTION;
fn->pn_arity = PN_FUNC;
fn->pn_pos.begin = pn->pn_pos.begin;
/*
* Set fn->pn_pos.end too, in case of error before we parse the
* closing brace. See bug 640075.
*/
fn->pn_pos.end = pn->pn_pos.end;
fn->pn_body = NULL;
fn->pn_cookie.makeFree();
@ -3155,7 +3208,7 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
if (!funbox)
return NULL;
JSFunction *fun = (JSFunction *) funbox->object;
JSFunction *fun = funbox->function();
/* Now parse formal argument list and compute fun->nargs. */
JSParseNode *prelude = NULL;
@ -3528,8 +3581,11 @@ Parser::statements()
*/
if (tc->atBodyLevel())
pn->pn_xflags |= PNX_FUNCDEFS;
else
else {
tc->flags |= TCF_HAS_FUNCTION_STMT;
/* Function statements extend the Call object at runtime. */
tc->noteHasExtensibleScope();
}
}
pn->append(pn2);
}
@ -3574,7 +3630,7 @@ MatchLabel(JSContext *cx, TokenStream *ts, JSParseNode *pn)
JSAtom *label;
TokenKind tt;
tt = ts->peekTokenSameLine();
tt = ts->peekTokenSameLine(TSF_OPERAND);
if (tt == TOK_ERROR)
return JS_FALSE;
if (tt == TOK_NAME) {
@ -4820,7 +4876,7 @@ Parser::returnOrYield(bool useAssignExpr)
if ((~tc->flags & (TCF_RETURN_EXPR | TCF_FUN_IS_GENERATOR)) == 0) {
/* As in Python (see PEP-255), disallow return v; in generators. */
ReportBadReturn(context, tc, JSREPORT_ERROR,
ReportBadReturn(context, tc, pn, JSREPORT_ERROR,
JSMSG_BAD_GENERATOR_RETURN,
JSMSG_BAD_ANON_GENERATOR_RETURN);
return NULL;
@ -4828,7 +4884,7 @@ Parser::returnOrYield(bool useAssignExpr)
if (context->hasStrictOption() &&
(~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0 &&
!ReportBadReturn(context, tc, JSREPORT_WARNING | JSREPORT_STRICT,
!ReportBadReturn(context, tc, pn, JSREPORT_WARNING | JSREPORT_STRICT,
JSMSG_NO_RETURN_VALUE,
JSMSG_ANON_NO_RETURN_VALUE)) {
return NULL;
@ -6220,7 +6276,6 @@ Parser::statement()
return pn;
}
case TOK_EOL:
case TOK_SEMI:
pn = UnaryNode::create(tc);
if (!pn)
@ -7486,7 +7541,7 @@ CheckForImmediatelyAppliedLambda(JSParseNode *pn)
JS_ASSERT(pn->pn_arity == PN_FUNC);
JSFunctionBox *funbox = pn->pn_funbox;
JS_ASSERT(((JSFunction *) funbox->object)->flags & JSFUN_LAMBDA);
JS_ASSERT((funbox->function())->flags & JSFUN_LAMBDA);
if (!(funbox->tcflags & (TCF_FUN_USES_ARGUMENTS | TCF_FUN_USES_OWN_NAME)))
pn->pn_dflags &= ~PND_FUNARG;
}
@ -7550,10 +7605,24 @@ Parser::memberExpr(JSBool allowCallSyntax)
return NULL;
#if JS_HAS_XML_SUPPORT
tt = tokenStream.getToken(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
/* Treat filters as 'with' statements for name deoptimization. */
JSParseNode *oldWith = tc->innermostWith;
JSStmtInfo stmtInfo;
if (tt == TOK_LP) {
tc->innermostWith = pn;
js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);
}
pn3 = primaryExpr(tt, JS_TRUE);
if (!pn3)
return NULL;
if (tt == TOK_LP) {
tc->innermostWith = oldWith;
PopStatement(tc);
}
/* Check both tt and pn_type, to distinguish |x.(y)| and |x.y::z| from |x.y|. */
if (tt == TOK_NAME && pn3->pn_type == TOK_NAME) {
pn2->pn_op = JSOP_GETPROP;
@ -7662,6 +7731,12 @@ Parser::memberExpr(JSBool allowCallSyntax)
pn2->pn_op = JSOP_EVAL;
tc->noteCallsEval();
tc->flags |= TCF_FUN_HEAVYWEIGHT;
/*
* In non-strict mode code, direct calls to eval can add
* variables to the call object.
*/
if (!tc->inStrictMode())
tc->noteHasExtensibleScope();
}
} else if (pn->pn_op == JSOP_GETPROP) {
/* Select JSOP_FUNAPPLY given foo.apply(...). */

Просмотреть файл

@ -974,6 +974,8 @@ struct JSFunctionBox : public JSObjectBox
level:JSFB_LEVEL_BITS;
uint32 tcflags;
JSFunction *function() const { return (JSFunction *) object; }
bool joinable() const;
/*
@ -982,6 +984,12 @@ struct JSFunctionBox : public JSObjectBox
*/
bool inAnyDynamicScope() const;
/*
* Must this function's descendants be marked as having an extensible
* ancestor?
*/
bool scopeIsExtensible() const;
/*
* Unbrand an object being initialized or constructed if any method cannot
* be joined to one compiler-created null closure shared among N different
@ -1052,6 +1060,7 @@ struct Parser : private js::AutoGCRooter
uint32 functionCount; /* number of functions in current unit */
JSObjectBox *traceListHead; /* list of parsed object for GC tracing */
JSTreeContext *tc; /* innermost tree context (stack-allocated) */
js::EmptyShape *emptyCallShape;/* empty shape for Call objects */
/* Root atoms and objects allocated for the parsed tree. */
js::AutoKeepAtoms keepAtoms;
@ -1110,6 +1119,7 @@ struct Parser : private js::AutoGCRooter
bool analyzeFunctions(JSTreeContext *tc);
void cleanFunctionList(JSFunctionBox **funbox);
bool markFunArgs(JSFunctionBox *funbox);
void markExtensibleScopeDescendants(JSFunctionBox *funbox, bool hasExtensibleParent);
void setFunctionKinds(JSFunctionBox *funbox, uint32 *tcflags);
void trace(JSTracer *trc);

Просмотреть файл

@ -61,6 +61,7 @@ PropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, uintN protoI
JS_ASSERT(this == &JS_PROPERTY_CACHE(cx));
JS_ASSERT(!cx->runtime->gcRunning);
JS_ASSERT_IF(adding, !obj->isCall());
if (js_IsPropertyCacheDisabled(cx)) {
PCMETER(disfills++);

Просмотреть файл

@ -79,9 +79,8 @@ PropertyTree::finish()
JS_FinishArenaPool(&arenaPool);
}
/* On failure, returns NULL. Does not report out of memory. */
Shape *
PropertyTree::newShapeUnchecked()
PropertyTree::newShape(JSContext *cx)
{
Shape *shape;
@ -90,8 +89,10 @@ PropertyTree::newShapeUnchecked()
shape->removeFree();
} else {
JS_ARENA_ALLOCATE_CAST(shape, Shape *, &arenaPool, sizeof(Shape));
if (!shape)
if (!shape) {
JS_ReportOutOfMemory(cx);
return NULL;
}
}
#ifdef DEBUG
@ -103,15 +104,6 @@ PropertyTree::newShapeUnchecked()
return shape;
}
Shape *
PropertyTree::newShape(JSContext *cx)
{
Shape *shape = newShapeUnchecked();
if (!shape)
JS_ReportOutOfMemory(cx);
return shape;
}
static KidsHash *
HashChildren(Shape *kid1, Shape *kid2)
{

Просмотреть файл

@ -122,7 +122,6 @@ class PropertyTree
bool init();
void finish();
js::Shape *newShapeUnchecked();
js::Shape *newShape(JSContext *cx);
js::Shape *getChild(JSContext *cx, js::Shape *parent, const js::Shape &child);

Просмотреть файл

@ -151,16 +151,18 @@ JSProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id,
if (desc.obj) {
if (desc.attrs & JSPROP_READONLY)
return true;
if (desc.setter && ((desc.attrs & JSPROP_SETTER) || desc.setter != StrictPropertyStub)) {
if (!desc.setter) {
desc.setter = StrictPropertyStub;
} else if ((desc.attrs & JSPROP_SETTER) || desc.setter != StrictPropertyStub) {
if (!CallSetter(cx, receiver, id, desc.setter, desc.attrs, desc.shortid, strict, vp))
return false;
if (!proxy->isProxy() || proxy->getProxyHandler() != this)
return true;
if (desc.attrs & JSPROP_SHARED)
return true;
}
if (!desc.getter)
desc.getter = PropertyStub;
if (!desc.setter)
desc.setter = StrictPropertyStub;
desc.value = *vp;
return defineProperty(cx, receiver, id, &desc);
}
@ -169,16 +171,18 @@ JSProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id,
if (desc.obj) {
if (desc.attrs & JSPROP_READONLY)
return true;
if (desc.setter && ((desc.attrs & JSPROP_SETTER) || desc.setter != StrictPropertyStub)) {
if (!desc.setter) {
desc.setter = StrictPropertyStub;
} else if ((desc.attrs & JSPROP_SETTER) || desc.setter != StrictPropertyStub) {
if (!CallSetter(cx, receiver, id, desc.setter, desc.attrs, desc.shortid, strict, vp))
return false;
if (!proxy->isProxy() || proxy->getProxyHandler() != this)
return true;
if (desc.attrs & JSPROP_SHARED)
return true;
}
if (!desc.getter)
desc.getter = PropertyStub;
if (!desc.setter)
desc.setter = StrictPropertyStub;
return defineProperty(cx, receiver, id, &desc);
}
@ -376,7 +380,7 @@ ParsePropertyDescriptorObject(JSContext *cx, JSObject *obj, jsid id, const Value
{
AutoPropDescArrayRooter descs(cx);
PropDesc *d = descs.append();
if (!d || !d->initialize(cx, id, v))
if (!d || !d->initialize(cx, v))
return false;
desc->obj = obj;
desc->value = d->value;
@ -958,11 +962,6 @@ proxy_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool
static void
proxy_TraceObject(JSTracer *trc, JSObject *obj)
{
JSContext *cx = trc->context;
if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList))
js_TraceWatchPoints(trc, obj);
obj->getProxyHandler()->trace(trc, obj);
MarkValue(trc, obj->getProxyPrivate(), "private");
MarkValue(trc, obj->getProxyExtra(), "extra");
@ -972,6 +971,14 @@ proxy_TraceObject(JSTracer *trc, JSObject *obj)
}
}
static void
proxy_TraceFunction(JSTracer *trc, JSObject *obj)
{
proxy_TraceObject(trc, obj);
MarkValue(trc, GetCall(obj), "call");
MarkValue(trc, GetConstruct(obj), "construct");
}
static void
proxy_Finalize(JSContext *cx, JSObject *obj)
{
@ -1008,14 +1015,14 @@ JS_FRIEND_API(Class) ObjectProxyClass = {
EnumerateStub,
ResolveStub,
ConvertStub,
NULL, /* finalize */
proxy_Finalize, /* finalize */
NULL, /* reserved0 */
NULL, /* checkAccess */
NULL, /* call */
NULL, /* construct */
NULL, /* xdrObject */
proxy_HasInstance, /* hasInstance */
NULL, /* mark */
proxy_TraceObject, /* trace */
JS_NULL_CLASS_EXT,
{
proxy_LookupProperty,
@ -1027,10 +1034,9 @@ JS_FRIEND_API(Class) ObjectProxyClass = {
proxy_DeleteProperty,
NULL, /* enumerate */
proxy_TypeOf,
proxy_TraceObject,
NULL, /* fix */
NULL, /* thisObject */
proxy_Finalize, /* clear */
NULL, /* clear */
}
};
@ -1044,14 +1050,14 @@ JS_FRIEND_API(Class) OuterWindowProxyClass = {
EnumerateStub,
ResolveStub,
ConvertStub,
NULL, /* finalize */
proxy_Finalize, /* finalize */
NULL, /* reserved0 */
NULL, /* checkAccess */
NULL, /* call */
NULL, /* construct */
NULL, /* xdrObject */
NULL, /* hasInstance */
NULL, /* mark */
proxy_TraceObject, /* trace */
{
NULL, /* equality */
NULL, /* outerObject */
@ -1068,10 +1074,9 @@ JS_FRIEND_API(Class) OuterWindowProxyClass = {
proxy_DeleteProperty,
NULL, /* enumerate */
NULL, /* typeof */
proxy_TraceObject,
NULL, /* fix */
NULL, /* thisObject */
proxy_Finalize, /* clear */
NULL, /* clear */
}
};
@ -1111,7 +1116,7 @@ JS_FRIEND_API(Class) FunctionProxyClass = {
proxy_Construct,
NULL, /* xdrObject */
js_FunctionClass.hasInstance,
NULL, /* mark */
proxy_TraceFunction, /* trace */
JS_NULL_CLASS_EXT,
{
proxy_LookupProperty,
@ -1123,7 +1128,6 @@ JS_FRIEND_API(Class) FunctionProxyClass = {
proxy_DeleteProperty,
NULL, /* enumerate */
proxy_TypeOf,
proxy_TraceObject,
NULL, /* fix */
NULL, /* thisObject */
NULL, /* clear */

Просмотреть файл

@ -85,6 +85,7 @@ typedef struct JSFunctionBox JSFunctionBox;
typedef struct JSObjectBox JSObjectBox;
typedef struct JSParseNode JSParseNode;
typedef struct JSProperty JSProperty;
typedef struct JSScript JSScript;
typedef struct JSSharpObjectMap JSSharpObjectMap;
typedef struct JSThread JSThread;
typedef struct JSThreadData JSThreadData;
@ -171,6 +172,7 @@ struct PropertyCacheEntry;
struct Shape;
struct EmptyShape;
class Bindings;
} /* namespace js */

Просмотреть файл

@ -156,7 +156,6 @@ typedef struct JSPropertyDescriptor JSPropertyDescriptor;
typedef struct JSPropertySpec JSPropertySpec;
typedef struct JSObjectMap JSObjectMap;
typedef struct JSRuntime JSRuntime;
typedef struct JSScript JSScript;
typedef struct JSStackFrame JSStackFrame;
typedef struct JSXDRState JSXDRState;
typedef struct JSExceptionState JSExceptionState;
@ -340,14 +339,6 @@ typedef JSBool
typedef JSBool
(* JSHasInstanceOp)(JSContext *cx, JSObject *obj, const jsval *v, JSBool *bp);
/*
* Deprecated function type for JSClass.mark. All new code should define
* JSTraceOp instead to ensure the traversal of traceable things stored in
* the native structures.
*/
typedef uint32
(* JSMarkOp)(JSContext *cx, JSObject *obj, void *arg);
/*
* Function type for trace operation of the class called to enumerate all
* traceable things reachable from obj's private data structure. For each such
@ -364,27 +355,10 @@ typedef uint32
* the traversal is a part of the marking phase through calling
* JS_IsGCMarkingTracer and apply a special code like emptying caches or
* marking its native structures.
*
* To define the tracer for a JSClass, the implementation must add
* JSCLASS_MARK_IS_TRACE to class flags and use JS_CLASS_TRACE(method)
* macro below to convert JSTraceOp to JSMarkOp when initializing or
* assigning JSClass.mark field.
*/
typedef void
(* JSTraceOp)(JSTracer *trc, JSObject *obj);
#if defined __GNUC__ && __GNUC__ >= 4 && !defined __cplusplus
# define JS_CLASS_TRACE(method) \
(__builtin_types_compatible_p(JSTraceOp, __typeof(&(method))) \
? (JSMarkOp)(method) \
: js_WrongTypeForClassTracer)
extern JSMarkOp js_WrongTypeForClassTracer;
#else
# define JS_CLASS_TRACE(method) ((JSMarkOp)(method))
#endif
/*
* Tracer callback, called for each traceable thing directly referenced by a
* particular object or runtime structure. It is the callback responsibility
@ -467,12 +441,6 @@ typedef void
typedef JSBool
(* JSOperationCallback)(JSContext *cx);
/*
* Deprecated form of JSOperationCallback.
*/
typedef JSBool
(* JSBranchCallback)(JSContext *cx, JSScript *script);
typedef void
(* JSErrorReporter)(JSContext *cx, const char *message, JSErrorReport *report);

Просмотреть файл

@ -592,7 +592,7 @@ class NodeBuilder
bool xmlFilterExpression(Value left, Value right, TokenPos *pos, Value *dst);
bool xmlAttributeSelector(Value expr, TokenPos *pos, Value *dst);
bool xmlAttributeSelector(Value expr, bool computed, TokenPos *pos, Value *dst);
bool xmlQualifiedIdentifier(Value left, Value right, bool computed, TokenPos *pos, Value *dst);
@ -1454,13 +1454,16 @@ NodeBuilder::xmlDefaultNamespace(Value ns, TokenPos *pos, Value *dst)
}
bool
NodeBuilder::xmlAttributeSelector(Value expr, TokenPos *pos, Value *dst)
NodeBuilder::xmlAttributeSelector(Value expr, bool computed, TokenPos *pos, Value *dst)
{
Value cb = callbacks[AST_XMLATTR_SEL];
if (!cb.isNull())
return callback(cb, expr, pos, dst);
return callback(cb, expr, BooleanValue(computed), pos, dst);
return newNode(AST_XMLATTR_SEL, pos, "attribute", expr, dst);
return newNode(AST_XMLATTR_SEL, pos,
"attribute", expr,
"computed", BooleanValue(computed),
dst);
}
bool
@ -1838,7 +1841,7 @@ ASTSerializer::statements(JSParseNode *pn, NodeVector &elts)
Value elt;
if (!sourceElement(next, &elt))
return false;
JS_ALWAYS_TRUE(elts.append(elt)); /* space check above */
elts.infallibleAppend(elt);
}
return true;
@ -1854,7 +1857,7 @@ ASTSerializer::expressions(JSParseNode *pn, NodeVector &elts)
Value elt;
if (!expression(next, &elt))
return false;
JS_ALWAYS_TRUE(elts.append(elt)); /* space check above */
elts.infallibleAppend(elt);
}
return true;
@ -1870,7 +1873,7 @@ ASTSerializer::xmls(JSParseNode *pn, NodeVector &elts)
Value elt;
if (!xml(next, &elt))
return false;
JS_ALWAYS_TRUE(elts.append(elt)); /* space check above */
elts.infallibleAppend(elt);
}
return true;
@ -1935,8 +1938,6 @@ ASTSerializer::variableDeclaration(JSParseNode *pn, bool let, Value *dst)
VarDeclKind kind = let ? VARDECL_LET : VARDECL_VAR;
NodeVector dtors(cx);
if (!dtors.reserve(pn->pn_count))
return false;
/* In a for-in context, variable declarations contain just a single pattern. */
if (pn->pn_xflags & PNX_FORINVAR) {
@ -1947,11 +1948,13 @@ ASTSerializer::variableDeclaration(JSParseNode *pn, bool let, Value *dst)
builder.variableDeclaration(dtors, kind, &pn->pn_pos, dst);
}
if (!dtors.reserve(pn->pn_count))
return false;
for (JSParseNode *next = pn->pn_head; next; next = next->pn_next) {
Value child;
if (!variableDeclarator(next, &kind, &child))
return false;
JS_ALWAYS_TRUE(dtors.append(child)); /* space check above */
dtors.infallibleAppend(child);
}
return builder.variableDeclaration(dtors, kind, &pn->pn_pos, dst);
@ -1997,7 +2000,7 @@ ASTSerializer::letHead(JSParseNode *pn, NodeVector &dtors)
*/
if (!variableDeclarator(next, &kind, &child))
return false;
JS_ALWAYS_TRUE(dtors.append(child)); /* space check above */
dtors.infallibleAppend(child);
}
return true;
@ -2045,7 +2048,7 @@ ASTSerializer::switchStatement(JSParseNode *pn, Value *dst)
#endif
if (!switchCase(next, &child))
return false;
JS_ALWAYS_TRUE(cases.append(child)); /* space check above */
cases.infallibleAppend(child);
}
return builder.switchStatement(disc, cases, lexical, &pn->pn_pos, dst);
@ -2078,7 +2081,7 @@ ASTSerializer::tryStatement(JSParseNode *pn, Value *dst)
Value clause;
if (!catchClause(next->pn_expr, &clause))
return false;
JS_ALWAYS_TRUE(clauses.append(clause)); /* space check above */
clauses.infallibleAppend(clause);
}
}
@ -2316,57 +2319,36 @@ bool
ASTSerializer::leftAssociate(JSParseNode *pn, Value *dst)
{
JS_ASSERT(pn->pn_arity == PN_LIST);
const size_t len = pn->pn_count;
JS_ASSERT(len >= 1);
if (len == 1)
return expression(pn->pn_head, dst);
JS_ASSERT(len >= 2);
Vector<JSParseNode *, 8> list(cx);
if (!list.reserve(len))
return false;
for (JSParseNode *next = pn->pn_head; next; next = next->pn_next) {
JS_ALWAYS_TRUE(list.append(next)); /* space check above */
}
JS_ASSERT(pn->pn_count >= 1);
TokenKind tk = PN_TYPE(pn);
bool lor = tk == TOK_OR;
bool logop = lor || (tk == TOK_AND);
Value right;
if (!expression(list[len - 1], &right))
JSParseNode *head = pn->pn_head;
Value left;
if (!expression(head, &left))
return false;
size_t i = len - 2;
do {
JSParseNode *next = list[i];
Value left;
if (!expression(next, &left))
for (JSParseNode *next = head->pn_next; next; next = next->pn_next) {
Value right;
if (!expression(next, &right))
return false;
TokenPos subpos = { next->pn_pos.begin, pn->pn_pos.end };
TokenPos subpos = { pn->pn_pos.begin, next->pn_pos.end };
if (logop) {
if (!builder.logicalExpression(lor, left, right, &subpos, &right))
if (!builder.logicalExpression(lor, left, right, &subpos, &left))
return false;
} else {
BinaryOperator op = binop(PN_TYPE(pn), PN_OP(pn));
LOCAL_ASSERT(op > BINOP_ERR && op < BINOP_LIMIT);
if (!builder.binaryExpression(op, left, right, &subpos, &right))
if (!builder.binaryExpression(op, left, right, &subpos, &left))
return false;
}
} while (i-- != 0);
}
*dst = right;
*dst = left;
return true;
}
@ -2594,7 +2576,7 @@ ASTSerializer::expression(JSParseNode *pn, Value *dst)
Value arg;
if (!expression(next, &arg))
return false;
JS_ALWAYS_TRUE(args.append(arg)); /* space check above */
args.infallibleAppend(arg);
}
return PN_TYPE(pn) == TOK_NEW
@ -2613,9 +2595,15 @@ ASTSerializer::expression(JSParseNode *pn, Value *dst)
case TOK_LB:
{
Value left, right;
bool computed = true;
#ifdef JS_HAS_XML_SUPPORT
computed = (PN_TYPE(pn->pn_right) != TOK_DBLCOLON &&
PN_TYPE(pn->pn_right) != TOK_ANYNAME &&
PN_TYPE(pn->pn_right) != TOK_AT);
#endif
return expression(pn->pn_left, &left) &&
expression(pn->pn_right, &right) &&
builder.memberExpression(true, left, right, &pn->pn_pos, dst);
builder.memberExpression(computed, left, right, &pn->pn_pos, dst);
}
case TOK_RB:
@ -2626,12 +2614,12 @@ ASTSerializer::expression(JSParseNode *pn, Value *dst)
for (JSParseNode *next = pn->pn_head; next; next = next->pn_next) {
if (PN_TYPE(next) == TOK_COMMA) {
JS_ALWAYS_TRUE(elts.append(MagicValue(JS_SERIALIZE_NO_NODE))); /* space check above */
elts.infallibleAppend(MagicValue(JS_SERIALIZE_NO_NODE));
} else {
Value expr;
if (!expression(next, &expr))
return false;
JS_ALWAYS_TRUE(elts.append(expr)); /* space check above */
elts.infallibleAppend(expr);
}
}
@ -2648,7 +2636,7 @@ ASTSerializer::expression(JSParseNode *pn, Value *dst)
Value prop;
if (!property(next, &prop))
return false;
JS_ALWAYS_TRUE(elts.append(prop)); /* space check above */
elts.infallibleAppend(prop);
}
return builder.objectExpression(elts, &pn->pn_pos, dst);
@ -2736,8 +2724,12 @@ ASTSerializer::expression(JSParseNode *pn, Value *dst)
case TOK_AT:
{
Value expr;
return expression(pn->pn_kid, &expr) &&
builder.xmlAttributeSelector(expr, &pn->pn_pos, dst);
JSParseNode *kid = pn->pn_kid;
bool computed = ((PN_TYPE(kid) != TOK_NAME || PN_OP(kid) != JSOP_QNAMEPART) &&
PN_TYPE(kid) != TOK_DBLCOLON &&
PN_TYPE(kid) != TOK_ANYNAME);
return expression(kid, &expr) &&
builder.xmlAttributeSelector(expr, computed, &pn->pn_pos, dst);
}
case TOK_FILTER:
@ -2943,12 +2935,12 @@ ASTSerializer::arrayPattern(JSParseNode *pn, VarDeclKind *pkind, Value *dst)
for (JSParseNode *next = pn->pn_head; next; next = next->pn_next) {
if (PN_TYPE(next) == TOK_COMMA) {
JS_ALWAYS_TRUE(elts.append(MagicValue(JS_SERIALIZE_NO_NODE))); /* space check above */
elts.infallibleAppend(MagicValue(JS_SERIALIZE_NO_NODE));
} else {
Value patt;
if (!pattern(next, pkind, &patt))
return false;
JS_ALWAYS_TRUE(elts.append(patt)); /* space check above */
elts.infallibleAppend(patt);
}
}
@ -2974,7 +2966,7 @@ ASTSerializer::objectPattern(JSParseNode *pn, VarDeclKind *pkind, Value *dst)
return false;
}
JS_ALWAYS_TRUE(elts.append(prop)); /* space check above */
elts.infallibleAppend(prop);
}
return builder.objectPattern(elts, &pn->pn_pos, dst);

Просмотреть файл

@ -62,7 +62,6 @@
#ifdef JS_TRACER
#include "jstracer.h"
using namespace avmplus;
using namespace nanojit;
#endif
@ -94,7 +93,7 @@ resc_trace(JSTracer *trc, JSObject *obj)
Class js::regexp_statics_class = {
"RegExpStatics",
JSCLASS_HAS_PRIVATE | JSCLASS_MARK_IS_TRACE,
JSCLASS_HAS_PRIVATE,
PropertyStub, /* addProperty */
PropertyStub, /* delProperty */
PropertyStub, /* getProperty */
@ -109,7 +108,7 @@ Class js::regexp_statics_class = {
NULL, /* construct */
NULL, /* xdrObject */
NULL, /* hasInstance */
JS_CLASS_TRACE(resc_trace)
resc_trace
};
/*
@ -477,12 +476,6 @@ regexp_finalize(JSContext *cx, JSObject *obj)
static JSBool
regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, Value *argv, JSBool test, Value *rval);
static JSBool
regexp_call(JSContext *cx, uintN argc, Value *vp)
{
return regexp_exec_sub(cx, &JS_CALLEE(cx, vp).toObject(), argc, JS_ARGV(cx, vp), false, vp);
}
#if JS_HAS_XDR
#include "jsxdrapi.h"
@ -556,7 +549,7 @@ js::Class js_RegExpClass = {
js_RegExp_str,
JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
JSCLASS_HAS_RESERVED_SLOTS(JSObject::REGEXP_CLASS_RESERVED_SLOTS) |
JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
PropertyStub, /* addProperty */
PropertyStub, /* delProperty */
PropertyStub, /* getProperty */
@ -567,11 +560,11 @@ js::Class js_RegExpClass = {
regexp_finalize,
NULL, /* reserved0 */
NULL, /* checkAccess */
regexp_call,
NULL, /* call */
NULL, /* construct */
js_XDRRegExpObject,
NULL, /* hasInstance */
JS_CLASS_TRACE(regexp_trace)
regexp_trace
};
/*
@ -595,9 +588,9 @@ js_regexp_toString(JSContext *cx, JSObject *obj, Value *vp)
if (size_t len = src->length()) {
if (!sb.reserve(len + 2))
return false;
JS_ALWAYS_TRUE(sb.append('/'));
JS_ALWAYS_TRUE(sb.append(src->chars(), len));
JS_ALWAYS_TRUE(sb.append('/'));
sb.infallibleAppend('/');
sb.infallibleAppend(src->chars(), len);
sb.infallibleAppend('/');
} else {
if (!sb.append("/(?:)/"))
return false;
@ -648,7 +641,7 @@ EscapeNakedForwardSlashes(JSContext *cx, JSString *unescaped)
if (!newChars.length()) {
if (!newChars.reserve(oldLen + 1))
return NULL;
JS_ALWAYS_TRUE(newChars.append(oldChars, size_t(it - oldChars)));
newChars.infallibleAppend(oldChars, size_t(it - oldChars));
}
if (!newChars.append('\\'))
return NULL;
@ -658,17 +651,17 @@ EscapeNakedForwardSlashes(JSContext *cx, JSString *unescaped)
return NULL;
}
if (newChars.length()) {
size_t len = newChars.length();
if (!newChars.append('\0'))
return NULL;
jschar *chars = newChars.extractRawBuffer();
JSString *escaped = js_NewString(cx, chars, len);
if (!escaped)
cx->free(chars);
return escaped;
}
return unescaped;
if (newChars.empty())
return unescaped;
size_t len = newChars.length();
if (!newChars.append('\0'))
return NULL;
jschar *chars = newChars.extractRawBuffer();
JSString *escaped = js_NewString(cx, chars, len);
if (!escaped)
cx->free(chars);
return escaped;
}
static bool

Просмотреть файл

@ -73,7 +73,7 @@ class RegExpStatics
void copyTo(RegExpStatics &dst) {
dst.matchPairs.clear();
/* 'save' has already reserved space in matchPairs */
JS_ALWAYS_TRUE(dst.matchPairs.append(matchPairs));
dst.matchPairs.infallibleAppend(matchPairs);
dst.matchPairsInput = matchPairsInput;
dst.pendingInput = pendingInput;
dst.flags = flags;
@ -138,10 +138,6 @@ class RegExpStatics
JS_ASSERT(pairNum < pairCount());
}
bool pairIsPresent(size_t pairNum) const {
return get(pairNum, 0) >= 0;
}
/* Precondition: paren is present. */
size_t getParenLength(size_t pairNum) const {
checkParenNum(pairNum);
@ -273,6 +269,10 @@ class RegExpStatics
JS_CALL_STRING_TRACER(trc, matchPairsInput, "res->matchPairsInput");
}
bool pairIsPresent(size_t pairNum) const {
return get(pairNum, 0) >= 0;
}
/* Value creators. */
bool createPendingInput(JSContext *cx, Value *out) const;

Просмотреть файл

@ -521,9 +521,9 @@ RegExp::compile(JSContext *cx)
StringBuffer sb(cx);
if (!sb.reserve(JS_ARRAY_LENGTH(prefix) + source->length() + JS_ARRAY_LENGTH(postfix)))
return false;
JS_ALWAYS_TRUE(sb.append(prefix, JS_ARRAY_LENGTH(prefix)));
JS_ALWAYS_TRUE(sb.append(source->chars(), source->length()));
JS_ALWAYS_TRUE(sb.append(postfix, JS_ARRAY_LENGTH(postfix)));
sb.infallibleAppend(prefix, JS_ARRAY_LENGTH(prefix));
sb.infallibleAppend(source->chars(), source->length());
sb.infallibleAppend(postfix, JS_ARRAY_LENGTH(postfix));
JSLinearString *fakeySource = sb.finishString();
if (!fakeySource)

Просмотреть файл

@ -175,16 +175,38 @@ TokenStream::init(const jschar *base, size_t length, const char *fn, uintN ln, J
version = v;
xml = VersionHasXML(v);
userbuf.base = (jschar *)base;
userbuf.limit = (jschar *)base + length;
userbuf.ptr = (jschar *)base;
linebase = userbuf.base;
userbuf.init(base, length);
linebase = base;
prevLinebase = NULL;
listener = cx->debugHooks->sourceHandler;
listenerData = cx->debugHooks->sourceHandlerData;
/*
* This table holds all the token kinds that satisfy these properties:
* - A single char long.
* - Cannot be a prefix of any longer token (eg. '+' is excluded because
* '+=' is a valid token).
* - Doesn't need tp->t_op set (eg. this excludes '~').
*
* The few token kinds satisfying these properties cover roughly 35--45%
* of the tokens seen in practice.
*
* Nb: oneCharTokens, maybeEOL and maybeStrSpecial could be static, but
* initializing them this way is a bit easier. Don't worry, the time to
* initialize them for each TokenStream is trivial. See bug 639420.
*/
memset(oneCharTokens, 0, sizeof(oneCharTokens));
oneCharTokens[';'] = TOK_SEMI;
oneCharTokens[','] = TOK_COMMA;
oneCharTokens['?'] = TOK_HOOK;
oneCharTokens['['] = TOK_LB;
oneCharTokens[']'] = TOK_RB;
oneCharTokens['{'] = TOK_LC;
oneCharTokens['}'] = TOK_RC;
oneCharTokens['('] = TOK_LP;
oneCharTokens[')'] = TOK_RP;
/* See getChar() for an explanation of maybeEOL[]. */
memset(maybeEOL, 0, sizeof(maybeEOL));
maybeEOL['\n'] = true;
@ -254,8 +276,8 @@ int32
TokenStream::getChar()
{
int32 c;
if (JS_LIKELY(userbuf.ptr < userbuf.limit)) {
c = *userbuf.ptr++;
if (JS_LIKELY(userbuf.hasRawChars())) {
c = userbuf.getRawChar();
/*
* Normalize the jschar if it was a newline. We need to detect any of
@ -276,10 +298,9 @@ TokenStream::getChar()
if (c == '\n')
goto eol;
if (c == '\r') {
if (userbuf.ptr < userbuf.limit && *userbuf.ptr == '\n') {
/* a \r\n sequence: treat as a single EOL, skip over the \n */
userbuf.ptr++;
}
/* if it's a \r\n sequence: treat as a single EOL, skip over the \n */
if (userbuf.hasRawChars())
userbuf.matchRawChar('\n');
goto eol;
}
if (c == LINE_SEPARATOR || c == PARA_SEPARATOR)
@ -293,20 +314,23 @@ TokenStream::getChar()
eol:
prevLinebase = linebase;
linebase = userbuf.ptr;
linebase = userbuf.addressOfNextRawChar();
lineno++;
return '\n';
}
/*
* This gets the next char. It does nothing special with EOL sequences, not
* even updating the line counters.
* even updating the line counters. It can be used safely if (a) the
* resulting char is guaranteed to be ungotten (by ungetCharIgnoreEOL()) if
* it's an EOL, and (b) the line-related state (lineno, linebase) is not used
* before it's ungotten.
*/
int32
TokenStream::getCharIgnoreEOL()
{
if (JS_LIKELY(userbuf.ptr < userbuf.limit))
return *userbuf.ptr++;
if (JS_LIKELY(userbuf.hasRawChars()))
return userbuf.getRawChar();
flags |= TSF_EOF;
return EOF;
@ -317,32 +341,34 @@ TokenStream::ungetChar(int32 c)
{
if (c == EOF)
return;
JS_ASSERT(userbuf.ptr > userbuf.base);
userbuf.ptr--;
JS_ASSERT(!userbuf.atStart());
userbuf.ungetRawChar();
if (c == '\n') {
#ifdef DEBUG
int32 c2 = *userbuf.ptr;
JS_ASSERT(c2 == '\n' || c2 == '\r' || c2 == LINE_SEPARATOR || c2 == PARA_SEPARATOR);
int32 c2 = userbuf.peekRawChar();
JS_ASSERT(TokenBuf::isRawEOLChar(c2));
#endif
if (userbuf.ptr > userbuf.base && *(userbuf.ptr - 1) == '\r')
userbuf.ptr--; /* also unget the \r in a \r\n sequence */
/* if it's a \r\n sequence, also unget the \r */
if (!userbuf.atStart())
userbuf.matchRawCharBackwards('\r');
JS_ASSERT(prevLinebase); /* we should never get more than one EOL char */
linebase = prevLinebase;
prevLinebase = NULL;
lineno--;
} else {
JS_ASSERT(*userbuf.ptr == c);
JS_ASSERT(userbuf.peekRawChar() == c);
}
}
void
TokenStream::ungetCharIgnoreEOL(int32 c)
{
JS_ASSERT(c == '\n' || c == '\r' || c == LINE_SEPARATOR || c == PARA_SEPARATOR || c == EOF);
if (c == EOF)
return;
JS_ASSERT(userbuf.ptr > userbuf.base);
userbuf.ptr--;
JS_ASSERT(!userbuf.atStart());
userbuf.ungetRawChar();
}
/*
@ -371,30 +397,27 @@ TokenStream::peekChars(intN n, jschar *cp)
return i == n;
}
jschar *
TokenStream::findEOL()
const jschar *
TokenStream::TokenBuf::findEOL()
{
TokenBuf tmpUserbuf = userbuf;
jschar *tmpLinebase = linebase;
jschar *tmpPrevLinebase = prevLinebase;
uintN tmpFlags = flags;
uintN tmpLineno = lineno;
const jschar *tmp = ptr;
#ifdef DEBUG
/*
* This is the one exception to the "TokenBuf isn't accessed after
* poisoning" rule -- we may end up calling findEOL() in order to set up
* an error.
*/
if (!tmp)
tmp = ptrWhenPoisoned;
#endif
while (true) {
int32 c = getChar();
if (c == '\n' || c == EOF)
if (tmp >= limit)
break;
if (TokenBuf::isRawEOLChar(*tmp++))
break;
}
jschar *linelimit = userbuf.ptr;
/* Need to restore everything changed by getChar(). */
userbuf = tmpUserbuf;
linebase = tmpLinebase;
prevLinebase = tmpPrevLinebase;
flags = tmpFlags;
lineno = tmpLineno;
return linelimit;
return tmp;
}
bool
@ -403,14 +426,12 @@ TokenStream::reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN erro
{
JSErrorReport report;
char *message;
size_t linelength;
jschar *linechars;
jschar *linelimit;
char *linebytes;
bool warning;
JSBool ok;
TokenPos *tp;
uintN index, i;
const TokenPos *tp;
uintN i;
JSErrorReporter onError;
if (JSREPORT_IS_STRICT(flags) && !cx->hasStrictOption())
@ -440,41 +461,44 @@ TokenStream::reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN erro
report.filename = filename;
if (pn) {
report.lineno = pn->pn_pos.begin.lineno;
if (report.lineno != lineno)
goto report;
tp = &pn->pn_pos;
} else {
/* Point to the current token, not the next one to get. */
tp = &tokens[cursor].pos;
tp = pn ? &pn->pn_pos : &currentToken().pos;
report.lineno = tp->begin.lineno;
/*
* Given a token, T, that we want to complain about: if T's (starting)
* lineno doesn't match TokenStream's lineno, that means we've scanned past
* the line that T starts on, which makes it hard to print some or all of
* T's (starting) line for context.
*
* So we don't even try, leaving report.linebuf and friends zeroed. This
* means that any error involving a multi-line token (eg. an unterminated
* multi-line string literal) won't have a context printed.
*/
if (report.lineno == lineno) {
size_t linelength = userbuf.findEOL() - linebase;
linechars = (jschar *)cx->malloc((linelength + 1) * sizeof(jschar));
if (!linechars) {
warning = false;
goto out;
}
memcpy(linechars, linebase, linelength * sizeof(jschar));
linechars[linelength] = 0;
linebytes = js_DeflateString(cx, linechars, linelength);
if (!linebytes) {
warning = false;
goto out;
}
/* Unicode and char versions of the offending source line, without final \n */
report.linebuf = linebytes;
report.uclinebuf = linechars;
/* The lineno check above means we should only see single-line tokens here. */
JS_ASSERT(tp->begin.lineno == tp->end.lineno);
report.tokenptr = report.linebuf + tp->begin.index;
report.uctokenptr = report.uclinebuf + tp->begin.index;
}
report.lineno = lineno;
linelimit = findEOL();
linelength = linelimit - linebase;
linechars = (jschar *)cx->malloc((linelength + 1) * sizeof(jschar));
if (!linechars) {
warning = false;
goto out;
}
memcpy(linechars, linebase, linelength * sizeof(jschar));
linechars[linelength] = 0;
linebytes = js_DeflateString(cx, linechars, linelength);
if (!linebytes) {
warning = false;
goto out;
}
report.linebuf = linebytes; /* the offending source line, without final \n */
index = (tp->begin.lineno == tp->end.lineno)
? tp->begin.index /* the column number of the start of the bad token */
: 0;
report.tokenptr = report.linebuf + index;
report.uclinebuf = linechars;
report.uctokenptr = report.uclinebuf + index;
/*
* If there's a runtime exception type associated with this error
@ -492,7 +516,6 @@ TokenStream::reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN erro
* XXX it'd probably be best if there was only one call to this
* function, but there seem to be two error reporter call points.
*/
report:
onError = cx->errorReporter;
/*
@ -771,27 +794,75 @@ TokenStream::newToken(ptrdiff_t adjust)
{
cursor = (cursor + 1) & ntokensMask;
Token *tp = &tokens[cursor];
tp->ptr = userbuf.ptr + adjust;
tp->ptr = userbuf.addressOfNextRawChar() + adjust;
tp->pos.begin.index = tp->ptr - linebase;
tp->pos.begin.lineno = tp->pos.end.lineno = lineno;
return tp;
}
static JS_ALWAYS_INLINE JSBool
ScanAsSpace(jschar c)
{
/* Treat little- and big-endian BOMs as whitespace for compatibility. */
if (JS_ISSPACE(c) || c == 0xfffe || c == 0xfeff)
return JS_TRUE;
return JS_FALSE;
}
JS_ALWAYS_INLINE JSAtom *
TokenStream::atomize(JSContext *cx, CharBuffer &cb)
{
return js_AtomizeChars(cx, cb.begin(), cb.length(), 0);
}
#ifdef DEBUG
bool
IsTokenSane(Token *tp)
{
/*
* Nb: TOK_EOL should never be used in an actual Token; it should only be
* returned as a TokenKind from peekTokenSameLine().
*/
if (tp->type < TOK_ERROR || tp->type >= TOK_LIMIT || tp->type == TOK_EOL)
return false;
if (tp->pos.begin.lineno == tp->pos.end.lineno) {
if (tp->pos.begin.index > tp->pos.end.index)
return false;
} else {
/* Only certain token kinds can be multi-line. */
switch (tp->type) {
case TOK_STRING:
case TOK_XMLATTR:
case TOK_XMLSPACE:
case TOK_XMLTEXT:
case TOK_XMLCOMMENT:
case TOK_XMLCDATA:
case TOK_XMLPI:
break;
default:
return false;
}
}
return true;
}
#endif
bool
TokenStream::putIdentInTokenbuf(const jschar *identStart)
{
int32 c, qc;
const jschar *tmp = userbuf.addressOfNextRawChar();
userbuf.setAddressOfNextRawChar(identStart);
tokenbuf.clear();
for (;;) {
c = getCharIgnoreEOL();
if (!JS_ISIDENT(c)) {
if (c != '\\' || !matchUnicodeEscapeIdent(&qc))
break;
c = qc;
}
if (!tokenbuf.append(c)) {
userbuf.setAddressOfNextRawChar(tmp);
return false;
}
}
userbuf.setAddressOfNextRawChar(tmp);
return true;
}
TokenKind
TokenStream::getTokenInternal()
{
@ -800,6 +871,7 @@ TokenStream::getTokenInternal()
Token *tp;
JSAtom *atom;
bool hadUnicodeEscape;
const jschar *numStart;
#if JS_HAS_XML_SUPPORT
JSBool inTarget;
size_t targetLength;
@ -810,7 +882,6 @@ TokenStream::getTokenInternal()
/*
* Look for XML text.
*/
if (flags & TSF_XMLTEXTMODE) {
tt = TOK_XMLSPACE; /* veto if non-space, return TOK_XMLTEXT */
tp = newToken(0);
@ -848,7 +919,6 @@ TokenStream::getTokenInternal()
/*
* Look for XML tags.
*/
if (flags & TSF_XMLTAGMODE) {
tp = newToken(0);
c = getChar();
@ -982,43 +1052,48 @@ TokenStream::getTokenInternal()
c = getChar();
if (c == '\n') {
flags &= ~TSF_DIRTYLINE;
if (flags & TSF_NEWLINES)
break;
flags |= TSF_EOL;
continue;
}
} while (ScanAsSpace((jschar)c));
} while (JS_ISSPACE_OR_BOM((jschar)c));
tp = newToken(-1);
if (c == EOF) {
tp = newToken(0); /* no -1 here because userbuf.ptr isn't incremented for EOF */
tt = TOK_EOF;
goto out;
}
tp = newToken(-1);
/*
* Look for a one-char token; they're common and simple.
*/
if (c < 128) {
tt = (TokenKind)oneCharTokens[c];
if (tt != 0)
goto out;
}
/*
* Look for an identifier.
*/
hadUnicodeEscape = false;
if (JS_ISIDSTART(c) ||
(c == '\\' && (hadUnicodeEscape = matchUnicodeEscapeIdStart(&qc))))
{
if (hadUnicodeEscape)
const jschar *identStart = userbuf.addressOfNextRawChar() - 1;
if (hadUnicodeEscape) {
c = qc;
tokenbuf.clear();
identStart -= 5; /* account for the length of the escape */
}
for (;;) {
if (!tokenbuf.append(c))
goto error;
c = getChar();
if (c == '\\') {
if (!matchUnicodeEscapeIdent(&qc))
c = getCharIgnoreEOL();
if (!JS_ISIDENT(c)) {
if (c != '\\' || !matchUnicodeEscapeIdent(&qc))
break;
c = qc;
hadUnicodeEscape = true;
} else {
if (!JS_ISIDENT(c))
break;
}
}
ungetChar(c);
ungetCharIgnoreEOL(c);
/*
* Check for keywords unless we saw Unicode escape or parser asks
@ -1027,7 +1102,7 @@ TokenStream::getTokenInternal()
const KeywordInfo *kw;
if (!hadUnicodeEscape &&
!(flags & TSF_KEYWORD_IS_NAME) &&
(kw = FindKeyword(tokenbuf.begin(), tokenbuf.length()))) {
(kw = FindKeyword(identStart, userbuf.addressOfNextRawChar() - identStart))) {
if (kw->tokentype == TOK_RESERVED) {
if (!ReportCompileErrorNumber(cx, this, NULL, JSREPORT_ERROR,
JSMSG_RESERVED_ID, kw->chars)) {
@ -1061,7 +1136,17 @@ TokenStream::getTokenInternal()
}
}
atom = atomize(cx, tokenbuf);
/*
* Identifiers containing no Unicode escapes can be atomized directly
* from userbuf. The rest must have the escapes converted via
* tokenbuf before atomizing.
*/
if (!hadUnicodeEscape)
atom = js_AtomizeChars(cx, identStart, userbuf.addressOfNextRawChar() - identStart, 0);
else if (putIdentInTokenbuf(identStart))
atom = atomize(cx, tokenbuf);
else
atom = NULL;
if (!atom)
goto error;
tp->t_op = JSOP_NAME;
@ -1071,104 +1156,56 @@ TokenStream::getTokenInternal()
}
/*
* Look for a number.
* Look for a decimal number.
*/
if (JS7_ISDECNZ(c) || (c == '.' && JS7_ISDEC(peekChar()))) {
numStart = userbuf.addressOfNextRawChar() - 1;
if (JS7_ISDEC(c) || (c == '.' && JS7_ISDEC(peekChar()))) {
int radix = 10;
tokenbuf.clear();
decimal:
bool hasFracOrExp = false;
while (JS7_ISDEC(c))
c = getCharIgnoreEOL();
if (c == '0') {
c = getChar();
if (JS_TOLOWER(c) == 'x') {
radix = 16;
c = getChar();
if (!JS7_ISHEX(c)) {
ReportCompileErrorNumber(cx, this, NULL, JSREPORT_ERROR,
JSMSG_MISSING_HEXDIGITS);
goto error;
}
} else if (JS7_ISDEC(c)) {
radix = 8;
}
if (c == '.') {
hasFracOrExp = true;
do {
c = getCharIgnoreEOL();
} while (JS7_ISDEC(c));
}
while (JS7_ISHEX(c)) {
if (radix < 16) {
if (JS7_ISLET(c))
break;
if (radix == 8) {
/* Octal integer literals are not permitted in strict mode code. */
if (!ReportStrictModeError(cx, this, NULL, NULL, JSMSG_DEPRECATED_OCTAL))
goto error;
/*
* Outside strict mode, we permit 08 and 09 as decimal numbers, which
* makes our behaviour a superset of the ECMA numeric grammar. We
* might not always be so permissive, so we warn about it.
*/
if (c >= '8') {
if (!ReportCompileErrorNumber(cx, this, NULL, JSREPORT_WARNING,
JSMSG_BAD_OCTAL, c == '8' ? "08" : "09")) {
goto error;
}
radix = 10;
}
}
}
if (!tokenbuf.append(c))
if (c == 'e' || c == 'E') {
hasFracOrExp = true;
c = getCharIgnoreEOL();
if (c == '+' || c == '-')
c = getCharIgnoreEOL();
if (!JS7_ISDEC(c)) {
ungetCharIgnoreEOL(c);
ReportCompileErrorNumber(cx, this, NULL, JSREPORT_ERROR,
JSMSG_MISSING_EXPONENT);
goto error;
c = getChar();
}
if (radix == 10 && (c == '.' || JS_TOLOWER(c) == 'e')) {
if (c == '.') {
do {
if (!tokenbuf.append(c))
goto error;
c = getChar();
} while (JS7_ISDEC(c));
}
if (JS_TOLOWER(c) == 'e') {
if (!tokenbuf.append(c))
goto error;
c = getChar();
if (c == '+' || c == '-') {
if (!tokenbuf.append(c))
goto error;
c = getChar();
}
if (!JS7_ISDEC(c)) {
ReportCompileErrorNumber(cx, this, NULL, JSREPORT_ERROR,
JSMSG_MISSING_EXPONENT);
goto error;
}
do {
if (!tokenbuf.append(c))
goto error;
c = getChar();
} while (JS7_ISDEC(c));
}
do {
c = getCharIgnoreEOL();
} while (JS7_ISDEC(c));
}
ungetCharIgnoreEOL(c);
if (JS_ISIDSTART(c)) {
ReportCompileErrorNumber(cx, this, NULL, JSREPORT_ERROR, JSMSG_IDSTART_AFTER_NUMBER);
goto error;
}
/* Put back the next char and NUL-terminate tokenbuf for js_strto*. */
ungetChar(c);
if (!tokenbuf.append(0))
goto error;
/*
* Unlike identifiers and strings, numbers cannot contain escaped
* chars, so we don't need to use tokenbuf. Instead we can just
* convert the jschars in userbuf directly to the numeric value.
*/
jsdouble dval;
const jschar *dummy;
if (radix == 10) {
if (!js_strtod(cx, tokenbuf.begin(), tokenbuf.end(), &dummy, &dval))
if (!hasFracOrExp) {
if (!GetPrefixInteger(cx, numStart, userbuf.addressOfNextRawChar(), 10, &dummy, &dval))
goto error;
} else {
if (!GetPrefixInteger(cx, tokenbuf.begin(), tokenbuf.end(), radix, &dummy, &dval))
if (!js_strtod(cx, numStart, userbuf.addressOfNextRawChar(), &dummy, &dval))
goto error;
}
tp->t_dval = dval;
@ -1176,10 +1213,71 @@ TokenStream::getTokenInternal()
goto out;
}
/*
* Look for a hexadecimal or octal number.
*/
if (c == '0') {
int radix;
c = getCharIgnoreEOL();
if (c == 'x' || c == 'X') {
radix = 16;
c = getCharIgnoreEOL();
if (!JS7_ISHEX(c)) {
ungetCharIgnoreEOL(c);
ReportCompileErrorNumber(cx, this, NULL, JSREPORT_ERROR, JSMSG_MISSING_HEXDIGITS);
goto error;
}
numStart = userbuf.addressOfNextRawChar() - 1;
while (JS7_ISHEX(c))
c = getCharIgnoreEOL();
} else if (JS7_ISDEC(c)) {
radix = 8;
numStart = userbuf.addressOfNextRawChar() - 1;
while (JS7_ISDEC(c)) {
/* Octal integer literals are not permitted in strict mode code. */
if (!ReportStrictModeError(cx, this, NULL, NULL, JSMSG_DEPRECATED_OCTAL))
goto error;
/*
* Outside strict mode, we permit 08 and 09 as decimal numbers,
* which makes our behaviour a superset of the ECMA numeric
* grammar. We might not always be so permissive, so we warn
* about it.
*/
if (c >= '8') {
if (!ReportCompileErrorNumber(cx, this, NULL, JSREPORT_WARNING,
JSMSG_BAD_OCTAL, c == '8' ? "08" : "09")) {
goto error;
}
goto decimal; /* use the decimal scanner for the rest of the number */
}
c = getCharIgnoreEOL();
}
} else {
/* '0' not followed by 'x', 'X' or a digit; scan as a decimal number. */
radix = 10;
numStart = userbuf.addressOfNextRawChar() - 1;
goto decimal;
}
ungetCharIgnoreEOL(c);
if (JS_ISIDSTART(c)) {
ReportCompileErrorNumber(cx, this, NULL, JSREPORT_ERROR, JSMSG_IDSTART_AFTER_NUMBER);
goto error;
}
jsdouble dval;
const jschar *dummy;
if (!GetPrefixInteger(cx, numStart, userbuf.addressOfNextRawChar(), radix, &dummy, &dval))
goto error;
tp->t_dval = dval;
tt = TOK_NUMBER;
goto out;
}
/*
* Look for a string.
*/
if (c == '"' || c == '\'') {
qc = c;
tokenbuf.clear();
@ -1256,9 +1354,7 @@ TokenStream::getTokenInternal()
}
break;
}
} else if (c == '\n' || c == '\r' || c == LINE_SEPARATOR || c == PARA_SEPARATOR ||
c == EOF)
{
} else if (TokenBuf::isRawEOLChar(c) || c == EOF) {
ungetCharIgnoreEOL(c);
ReportCompileErrorNumber(cx, this, NULL, JSREPORT_ERROR,
JSMSG_UNTERMINATED_STRING);
@ -1281,19 +1377,7 @@ TokenStream::getTokenInternal()
/*
* This handles everything else.
*/
switch (c) {
case '\n': tt = TOK_EOL; goto eol_out;
case ';': tt = TOK_SEMI; break;
case '[': tt = TOK_LB; break;
case ']': tt = TOK_RB; break;
case '{': tt = TOK_LC; break;
case '}': tt = TOK_RC; break;
case '(': tt = TOK_LP; break;
case ')': tt = TOK_RP; break;
case ',': tt = TOK_COMMA; break;
case '?': tt = TOK_HOOK; break;
case '.':
#if JS_HAS_XML_SUPPORT
if (matchChar(c))
@ -1574,7 +1658,7 @@ TokenStream::getTokenInternal()
cp[3] == 'n' &&
cp[4] == 'e') {
skipChars(5);
while ((c = getChar()) != '\n' && ScanAsSpace((jschar)c))
while ((c = getChar()) != '\n' && JS_ISSPACE_OR_BOM((jschar)c))
continue;
if (JS7_ISDEC(c)) {
line = JS7_UNDEC(c);
@ -1586,7 +1670,7 @@ TokenStream::getTokenInternal()
}
line = temp;
}
while (c != '\n' && ScanAsSpace((jschar)c))
while (c != '\n' && JS_ISSPACE_OR_BOM((jschar)c))
c = getChar();
i = 0;
if (c == '"') {
@ -1601,7 +1685,7 @@ TokenStream::getTokenInternal()
}
if (c == '"') {
while ((c = getChar()) != '\n' &&
ScanAsSpace((jschar)c)) {
JS_ISSPACE_OR_BOM((jschar)c)) {
continue;
}
}
@ -1650,10 +1734,9 @@ TokenStream::getTokenInternal()
JSMSG_UNTERMINATED_COMMENT);
goto error;
}
if ((flags & TSF_NEWLINES) && linenoBefore != lineno) {
if (linenoBefore != lineno) {
flags &= ~TSF_DIRTYLINE;
tt = TOK_EOL;
goto eol_out;
flags |= TSF_EOL;
}
cursor = (cursor - 1) & ntokensMask;
goto retry;
@ -1809,18 +1892,36 @@ TokenStream::getTokenInternal()
}
out:
JS_ASSERT(tt != TOK_EOL);
flags |= TSF_DIRTYLINE;
eol_out:
JS_ASSERT(tt < TOK_LIMIT);
tp->pos.end.index = userbuf.ptr - linebase;
tp->pos.end.index = userbuf.addressOfNextRawChar() - linebase;
tp->type = tt;
JS_ASSERT(IsTokenSane(tp));
return tt;
error:
tt = TOK_ERROR;
/*
* For erroneous multi-line tokens we won't have changed end.lineno (it'll
* still be equal to begin.lineno) so we revert end.index to be equal to
* begin.index + 1 (as if it's a 1-char token) to avoid having inconsistent
* begin/end positions. end.index isn't used in error messages anyway.
*/
flags |= TSF_ERROR;
goto out;
flags |= TSF_DIRTYLINE;
tp->pos.end.index = tp->pos.begin.index + 1;
tp->type = TOK_ERROR;
JS_ASSERT(IsTokenSane(tp));
#ifdef DEBUG
/*
* Poisoning userbuf on error establishes an invariant: once an erroneous
* token has been seen, userbuf will not be consulted again. This is true
* because the parser will either (a) deal with the TOK_ERROR token by
* aborting parsing immediately; or (b) if the TOK_ERROR token doesn't
* match what it expected, it will unget the token, and the next getToken()
* call will immediately return the just-gotten TOK_ERROR token again
* without consulting userbuf, thanks to the lookahead buffer.
*/
userbuf.poison();
#endif
return TOK_ERROR;
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше